I have a couple questions on applying spread operator on range and map. Refer to code below, error lines are marked.
(1) "assert" works on the updated range, but why doesnt "println" print it?
(2) when we say "*range" groovy can figure out and extend the range. So why doesnt "map" work also, why do we need to say ":map" ?
def range = (1..3)
println range // prints: [1,2,3]
assert [0,1,2,3] == [0,*range] // works ok
println [0, *range] // error
def map = [a:1, b:2]
assert [a:1, b:2, c:3] == [c:3, *:map] // works ok
assert [a:1, b:2, c:3] == [c:3, *map] // error
When you call:
println [0, *range]
it is trying to call getAt on a property println. You need to wrap the list in braces to help the parser out:
println( [ 0, *range ] )
And for the second error, * in this form is the spread operator. It is used to spread Lists.
You have a map, so need to use the spread map operator *: (as you have seen)
Related
I would like to remove duplicates maps equal to some conditions, I have the maps below:
def map = [[name: "111F", invoice:"40",bilD:"aaaa", title:null],[name: "111F", invoice:"40",bilD:"aaaa",title:"something"],[name: "111F", invoice:"40",bilD:"bbbb",title:"something"]]
I tried with the code below to do that, but its not working
def newMap = map.unique {a, b -> (a.name == b.name && a.invoice == b.invoice && a.bilD == b.bilD)}
How can I get a map like below
[
[name: "111F", invoice:"40",bilD:"aaaa",title:"somethingOrNull"],[name: "111F", invoice:"40",bilD:"bbbb",title:"something"]
]
The easiest way I can think of is, add all the elements to a set, then transform the resulting set back to list (note, that your variable map actually contains a list).
The shortest way I can think of would be calling:
def newMap = (map as Set) as List
Collection.unique(Closure) behaves differently depending on the number of parameters in the closure. When the closure has one parameter it expects...
...a value used for comparison (either using
Comparable#compareTo(java.lang.Object) or
Object#equals(java.lang.Object)).
However, if the closure contains two arguments, as shown in your example, then the closure must return an integer...
...with 0 indicating the items are not unique
Strangely, the two-argument closure behavior seems to be in exact opposite of how it's described. Here's a working example:
def maps = [
[name: "111F", invoice:"40",bilD:"aaaa"],
[name: "111F", invoice:"40",bilD:"aaaa"],
[name: "111F", invoice:"40",bilD:"bbbb"]
]
assert maps.unique(false) {a, b ->
a.name == b.name && a.invoice == b.invoice && a.bilD == b.bilD ? 0 : 1
} == [['name':'111F', 'invoice':'40', 'bilD':'aaaa'], ['name':'111F', 'invoice':'40', 'bilD':'bbbb']]
However, in your case, you can simply use unique() without the closure:
assert maps.unique(false) == [['name':'111F', 'invoice':'40', 'bilD':'aaaa'], ['name':'111F', 'invoice':'40', 'bilD':'bbbb']]
According to the Groovy docs:
Ranges allow you to create a list of sequential values. These can be used as Lists since Range extends java.util.List.
However, in my case I need the List to end up as a String, including the square brackets. I tried the following:
def myRange = 1..5
def myList = [1, 2, 3, 4, 5]
// this passes
assert myRange == myList
// both of the following fail!
assert myRange.toString() == myList.toString()
assert myRange.subList(0, 5).toString() == myList.toString()
Am I missing something?
Extending something does not mean that its toString will be the same.
If you have to get the same output as with a list, try
myRange.toList().toString()
Or
"[${myRange.join(',')}]"
Or (adding comment as an easy answer for you)
assert (1..5).toListString() == "[1, 2, 3, 4, 5]"
In Groovy,what is the difference between,
def a=1..5
def b= [*1..5]
def c=[1..5]
what does * in [*1..5] symbolize?
* represents a Spread Operator. Elaborating your example:
a = 1..5
b = [*1..5]
c = [1..5]
assert a.class.name == "groovy.lang.IntRange" //Is a range from 1 till 5
assert b.class.name == "java.util.ArrayList" //Spread the range in a list
assert c.class.name == "java.util.ArrayList" //Is a list
Extending #ataylor's explanation:
assert a.size() == 5
assert b.size() == 5
assert c.size() == 1
To reach each element in c you have to iterate over it (which is a range)
c.each{println it}
Groovy Goodness by Mr Haki has a detailed example of its usage.
When you put a range object in a list, you get a list with one element of type IntRange:
assert [1..5].size() == 1
By applying the spread operator it expands the range and you get a list with five elements, the actual integers the range represents:
assert [*1..5].size() == 5
Here * (spread operator) expands the range 1..5 and hence you get a list of integers in that range [1, 2, 3, 4, 5]
Is there a safe range operator for Groovy?
For instance if I have,
[1,2,3][0..10]
Groovy will throw a java.lang.IndexOutOfBoundsException:
Is there a index safe way to access this range? Or do I always have to check the collection size prior to running a range?
You can use take(n), which allows you to take up to a specific number of items, without error if there's too few in the collection:
def input = [1,2,3]
def result = input.take(10)
assert result == [1,2,3]
input = [1,2,3,4,5]
result = input.take(4)
assert result == [1,2,3,4]
If you need to start at an offset, you can use drop(n), which does not modify the original collection:
def input = [1,2,3,4,5]
def result = input.drop(2).take(2)
assert result == [3,4]
These are both safe against the size of the collection. If the list is too small in the last example, you may only have one or zero items in the collection.
The following Groovy code
lines = ['0','1','2','3','4','5']
println lines[1..lines.size()-1]
println lines[1..-1]
println lines[1..<lines.size()-1]
println lines[1..<-1]
println lines[1..<-2]
println lines[1..-2]
produces this output:
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4]
[1, 0]
[1, 2, 3, 4, 5]
[1, 2, 3, 4]
Since -1 is the index of the last element in the list, the first two make sense (ranges in Groovy include the end element instead of omitting it as everywhere else in Java :-( )
Line #3 is the desired output (list without first and last element).
I'm worried about the output #4: Why do I get [1, 0] for 1..-1?
Also [1, 2, 3, 4, 5] for the range 1..<-2 seems wrong.
Why does that happen?
The best way to take all elements but the last one, in my opinion, is to use the take method:
def list = ['a', 'b', 'c']
assert list.take(list.size() - 1) == ['a', 'b']
It behaves properly in the corner case where size == 1:
def list = ['one']
assert list.take(list.size() - 1) == []
Though I'd prefer it to throw an exception in the case size == 0, but the behavior is not that bad:
def list = []
assert list.take(list.size() - 1) == []
You can also use list[0..<list.size()-1] (your third example) and it will behave the same except for the empty list, in which case it will throw an ArrayIndexOutOfBoundsException, but i think is not as readable as the take counterpart.
Another acceptable solution is using list[0..-2] (your last example), which i think looks much more elegant, but unfortunately breaks when size == 1 with an ArrayIndexOutOfBoundsException.
In your examples (i'll assume that you meant to use 0 as the starting index instead of 1 if you wanted to include all elements but the last one):
lines[0..lines.size()-1] is equivalent to lines[0..-1] because the getAt(Range) method of lists will treat ranges with negative indexes the same way asgetAt(Integer) does, i.e. as accessing the (list.size() + negativeIndex)'th element of the list. Therefore list[0..-1] is the same as saying "from first element to last" and it's the same as copying the list; and list[-1..0] is the same as "from last to first" and it's equivalent to list.reverse() :)
The problem with the other non-inclusive range example is that the non-inclusive ranges are being evaluated before the list access and they evaluate to an incorrect inclusive range. For example, 0..<-2 evaluates to 0..-1, and that's why it's returning all the elements. 0..<-1 evaluates to 0..0 and it returns only the first element.
Notice that the empty range is a special case. It's denoted as 0..<0 and it doesn't have an inclusive equivalent (so Groovy won't do any magic conversion here). And that's why list[0..<list.size()-1] works when size == 1 (the range evaluates to the empty range) while list[0..-2] doesn't :)
Maybe this has changed since epideman wrote his answer, but you can get the whole list without the last element with 0..<-1:
assert ["foo"][0..<-1] == []
assert ["foo", "bar"][0..<-1] == ["foo"]
assert ["foo", "bar", "baz"][0..<-1] == ["foo", "bar"]
// blows up if empty, here take is better
assert [][0..<-1] == [] // BOOM
// if you want null safe, use take
assert [].take(-1) == []
This is with groovy 2.2.1.
Since Groovy 2.4 you can use the init() method:
lines = ['0','1','2','3','4','5']
assert lines.init() == ['0','1','2','3','4']