Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copying lists of lists in groovy

Tags:

groovy

I want to perform a copy and get two different objects so that I can work on the copy without impacting the original.

I have this code (groovy 2.0.5):

def a = [[1,5,2,1,1], ["one", "five", "two", "one", "one"]]
def b = a
b.add([6,6,6,6,6,6])
println a
println b

that produces:

[[1, 5, 2, 1, 1], [one, five, two, one, one], [6, 6, 6, 6, 6, 6]]
[[1, 5, 2, 1, 1], [one, five, two, one, one], [6, 6, 6, 6, 6, 6]]

seems like b and a are actually the same object

I can fix it in this way:

def a = [[1,5,2,1,1], ["one", "five", "two", "one", "one"]]
def b = []

a.each {
    b.add(it)
}

b.add([6,6,6,6,6])
println a
println b

that produces my wanted result:

[[1, 5, 2, 1, 1], [one, five, two, one, one]]
[[1, 5, 2, 1, 1], [one, five, two, one, one], [6, 6, 6, 6, 6]]

But now look at this, where I want the original object and a copy with unique and sorted elements:

def a = [[1,5,2,1,1], ["one", "five", "two", "one", "one"]]
def b = a

b.each {
    it.unique().sort()
}

println a
println b

that produces:

[[1, 2, 5], [five, one, two]]
[[1, 2, 5], [five, one, two]]

If I try the same fix this time it doesn't work:

def a = [[1,5,2,1,1], ["one", "five", "two", "one", "one"]]
def b = []

a.each { 
    b.add(it)
}

b.each {
    it.unique().sort()
}

println a
println b

that still produces:

[[1, 2, 5], [five, one, two]]
[[1, 2, 5], [five, one, two]]

What's going on ?

like image 897
Luca Avatar asked Nov 09 '12 10:11

Luca


2 Answers

Just calling b = a sets b to being the same instance of the list (containing the same list instances) as a

Calling the second method with the a.each { b.add(it) } means b points to a different instance of List, but the contents of b are the same instances of lists as in a

What you need is something like:

def b = a*.collect()

b.each {
  it.unique().sort()
}

So the a*.collect() makes a new instance of a list for each list in a

You can also do this in one line:

def b = a*.unique( false )*.sort( false ) 

Passing false to unique and sort stop those method changing the original lists, and force them to return new list instances.

(Indeed the observant amongst you will notice that the sort does not need false passed to it, as we already have a new instance thanks to unique)

like image 168
tim_yates Avatar answered Oct 15 '22 09:10

tim_yates


Groovy has Collection#collectNested, which is the recursive form of #collect. This will create copies of lists of arbitrary depth. Prior to version 1.8.1 it's called #collectAll.

final a = [
    'a', [ 'aa', 'ab', [ 'aba', 'abb', 'abc' ], 'ac', 'ad' ], 'b', 'c', 'd', 'e'
]

final b = a.collectNested { it }
b[0] = 'B'

final c = a.collectNested { it * 2 }
c[0] = 'C'

assert a as String ==
    '[a, [aa, ab, [aba, abb, abc], ac, ad], b, c, d, e]'

assert b as String ==
    '[B, [aa, ab, [aba, abb, abc], ac, ad], b, c, d, e]'

assert c as String ==
    '[C, [aaaa, abab, [abaaba, abbabb, abcabc], acac, adad], bb, cc, dd, ee]'
like image 43
Justin Piper Avatar answered Oct 15 '22 10:10

Justin Piper