Ranges
Ranges appear inclusively like 0..10 or half-exclusively like 0..<10. They are often enclosed in parentheses since the range operator has low precedence.
assert (0..10).contains(5)
assert (0.0..10.0).containsWithinBounds(3.5)
for (item in 0..10) { println item }
for (item in 10..0) { println item }
(0..<10).each { println it }
Integer ranges are often used for selecting sublists. Range boundaries can be of any type that defines previous(), next() and implements Comparable. Notable examples are String and Date.
Lists
Lists look like arrays but are of type java.util.List plus new methods.
[1,2,3,4] == (1..4)
[1,2,3] + [1] == [1,2,3,1]
[1,2,3] << 1 == [1,2,3,1]
[1,2,3,1] - [1] == [2,3]
[1,2,3] * 2 == [1,2,3,1,2,3]
[1,[2,3]].flatten() == [1,2,3]
[1,2,3].reverse() == [3,2,1]
[1,2,3].disjoint([4,5,6]) == true
[1,2,3].intersect([4,3,1]) == [3,1]
[1,2,3].collect{ it+3 } == [4,5,6]
[1,2,3,1].unique().size() == 3
[1,2,3,1].count(1) == 2
[1,2,3,4].min() == 1
[1,2,3,4].max() == 4
[1,2,3,4].sum() == 10
[4,2,1,3].sort() == [1,2,3,4]
[4,2,1,3].findAll{it%2 == 0} == [4,2]
def anims=['cat','kangaroo','koala']
anims[2] == 'koala'
def kanims = anims[1..2]
anims.findAll{it =~ /k.*/} ==kanims
anims.find{ it =~ /k.*/} ==kanims[0]
anims.grep(~/k.*/) ==kanims
The sort() method is often used and comes in three flavors:
Sort call |
Usage |
col.sort() |
natural sort for comparable objects |
col.sort { it.propname } |
applying the closure to each item before comparing the results |
col.sort { a,b -> a <=> b } |
closure defines a comparator for each comparison |
Lists can also be indexed with negative indexes and reversed ranges.
def list = [0,1,2]
assert list[-1] == 2
assert list[-1..0] == list.reverse()
assert list == [list.head()] + list.tail()
Sublist assignments can make a list grow or shrink and lists can contain varying data types.
list[1..2] = ['x','y','z']
assert list == [0,'x','y','z']
Maps
Maps are like lists that have an arbitrary type of key instead of integer. Therefore, the syntax is very much aligned.
def map = [a:0, b:1]
Maps can be accessed in a conventional square-bracket syntax or as if the key was a property of the map.
assert map['a'] == 0
assert map.b == 1
map['a'] = 'x'
map.b = 'y'
assert map == [a:'x', b:'y']
There is also an explicit get method that optionally takes a default value.
assert map.c == null
assert map.get('c',2) == 2
assert map.c == 2
Map iteration methods take the nature of Map.Entry objects into account.
map.each { entry ->
println entry.key
println entry.value
}
map.each { key, value ->
println "$key $value"
}
for (entry in map) {
println "$entry.key $entry.value"
}
GPath
Calling a property on a list returns a list of the property for each item in the list.
employees.address.town
returns a list of town objects.
To do the same with method calls, use the spread-dot operator.
employees*.bonus(2008)
calls the bonus method on each employee and stores the
result in a list.
Closures
Closures capture a piece of logic and the enclosing scope. They are first-class objects and can receive messages, can be returned from method calls, stored in fields, and used as arguments to a method call.
Use in method parameter
def forEach(int i, Closure yield){
for (x in 1..i) yield(x)
}
Use as last method argument
forEach(3) { num -> println num }
Construct and assign to local variable
def squareIt = { println it * it}
forEach(3, squareIt)
Bind leftmost closure param to fixed argument
def multIt = {x, y -> println x * y}
forEach 3, multIt.curry(2)
forEach 3, multIt.curry('-')
Closure parameter list examples:
Closure.isCase(b) sends b to the closure and returns the call result as boolean. Use as in
switch ('xy'){
case {it.startsWith('x')} :...
}
[0,1,2].grep { it%2 == 0 }
{{ parent.title || parent.header.title}}
{{ parent.tldr }}
{{ parent.linkDescription }}
{{ parent.urlSource.name }}