groovy collection api
TRANSCRIPT
Groovy Collection APITrygve Amundsen, Kodemaker
basics
groovy collections
assert "string".collect { it } == ['s','t','r','i','n','g'] // string
• Java Collection (List, Map, Set mfl.)
• Array
• String
• Regexp
• Iterable
• CharSequence
• Object!
assert 42.collect { it } == [42]
notation, lists
def emptyList = [] assert [] instanceof ArrayList
def list = [1,2,3]assert list[1..-1] == [2,3]assert list[-3..-2] == [1,2] assert list[2..1] == [3,2]
notation, maps
def emptyMap = [:]assert [:] instanceof LinkedHashMap // predictable order
assert [a:1, b:2] == ['a':1, 'b':2]
method(a:1, b:2) // illusion of named parameters
ranges are lists
assert (1..3) == [1,2,3]assert (1..<3) == [1,2]
assert (1..3) instanceof IntRange // groovy.langassert (1..3) instanceof List
notation, list operators
assert [1,2] << 3 == [1,2,3] // leftShift()assert [1,2]+3 == [1,2,3] // plus()assert [1,2]+[3,4] == [1,2,3,4]assert [1,2,3]-[3,4] == [1,2] // minus()
notation, spread operator
assert ["a","b","1"]*.isNumber() == [false,false,true]assert ["a","b","1"].number == [false,false,true]
basics, api
each
[1,2,3].each { println it}[1,2,3].each { item -> println item }
[1,2,3].eachWithIndex { item, i -> println "index ${i} contains ${item}" }[1,2,3].reverseEach { println it }
find (filters)
assert [1,2,3].findAll { it % 2 } == [1,3]
assert [1,2,3].find { it % 2 } == 1
assert [1,2,3].findIndexValues { it %2 } == [0,2]
collect (map)
assert [1,2,3].collect { it * it } == [1,4,9]
assert [1,[2,3],4].collectNested { it*it } == [1,[4,9],16]
assert [1,2,3].collectEntries { ['k' +it, it] } == [k1:1,k2:2,k3:3]
changedunchanged
chan
ged
unch
ange
dnu
mbe
r of
item
s
item content
eacheachWithIndexreverseEach
findAlluniquetaildrop/take
collectcollectEntriescollectNested
Obj
ect
bool
ean
max, min, sum, countjoininject
anyevery
method returns
countByfindResultscollectManysplitC
olle
ctio
n
one
item
mul
tiple
item
s
findheadfirst/last
sortgroupBy
stackpush/pop
setplusminusintersect subsequencespermutationscombinations
reducersassert [1,2,3].max() == 3assert [1,2,3].min() == 1assert [1,2,3].sum() == 6
assert [1,2,3].max{ -it } == 1assert [1,2,3].max{ a,b-> b<=>a } == 1
reducers, cont’d
assert [1,2,3].any{ it > 3 } == falseassert [1,2,3].every{ it < 4 } == true
assert [1,2,3].join(";") == "1;2;3"
reducers, inject
assert [1,2,3].inject(0) { acc, val -> acc+val } == 6assert [1,2,3].inject("0") { acc, val -> acc+val } == "0123"assert [1,2,3].inject([]) { acc, val -> acc+val } == [1,2,3]
assert [a:1,b:2].inject(0) { acc, key, val -> acc+val } == 3
inject -> reduce
Object.metaClass.reduce = {initial, closure-> delegate.inject(initial, closure) }
[1,2,3].reduce(0){acc,val->acc+val}
useful methods
groupBy, countBy
assert [1,2,3].groupBy{it < 2 ? 'small' : 'big'} == [small:[1], big:[2,3]]assert [1,2,3].countBy{it < 2 ? 'small' : 'big'} == [small:1, big:2]
set operations
assert [1,2]+3 == [1,2,3]assert [1,2]+[3,4] == [1,2,3,4]assert [1,2].plus([3,4]) == [1,2,3,4]
assert [1,2,3]-3 == [1,2]assert [1,2,3]-[2,3] == [1]assert [1,2,3].minus([2,3]) == [1]
assert [1,2,3].intersect([3,4]) == [3]assert [1,2,3].disjoint([4,5]) == true
assert [1,2,3].subsequences() == [[3], [1, 2, 3], [1], [2], [2, 3], [1, 3], [1, 2]] as Setassert [1,2,3].permutations() == [[1, 2, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2], [2, 1, 3], [1, 3, 2]] as Setassert [[1,2],[3,4]].combinations() == [[1, 3], [2, 3], [1, 4], [2, 4]]
Nested Collections
assert [1,[2,3],4].flatten() == [1,2,3,4]
assert [1,[2,3],4].collectNested { it+10 } == [11,[12,13],14]
assert [['a','b'], [1,2]].transpose() == [['a',1],['b',2]]
functional styleassert [1,2,3].head() == 1assert [1,2,3].tail() == [2,3]
assert [1,2,3].first() == 1assert [1,2,3].last() == 3
assert [1,2,3].take(2) == [1,2]assert [1,2,3].drop(2) == [3]
assert [1,2,3].split { it<3 } == [[1,2],[3]]
list as stackNOTE: mutates!
def list = [1,2,3]list.push(4)assert list == [1,2,3,4]assert list.pop() == 4assert list == [1,2,3]
findResults
[1,2,3].findAll { it > 1 } .collect { it*it }
[1,2,3].findResults { it > 1 ? it*it : null }
is identical to
assert [1,2,3].findResults { it > 1 ? it*it : null } == [4,9]
Other, possibly useful methods
assert [1,2,3].collectMany { [it**2, it**3] } == [1,1,4,8,9,27]
map withDefault
def newMap = [:].withDefault { [] }[a:1,b:2,c:2].each { key, val -> newMap[val] << key}assert newMap == [1:['a'], 2:['b','c']]
mutability
• all methods return new collections/objects
• a few exceptions exist, such as push/pop and sort(boolean mutate)
• objects and collections are generally mutable
• use asImmutable(), asSynchronized() for multithreading
• or better: use gpars (included in groovy jdk)
• (that’s a different talk - coming up on groovy meetup!)
doc & src
Groovy JDK doc
Groovy JDK doc
DefaultGroovyMethods
real-world example
params.findAll { key, val -> // filtrer kun diagnose-parametre key.startsWith(paramPrefix+"diagnoser")}.groupBy { key, val -> // grupper på rader key.substring(0,"diagnoser[0]".size())}.findAll { rowKey, rowMap -> // Fjern tomme rader rowMap.any { key, val -> val && val != "null" }}.inject([:]) { acc, key, val -> // ungroup rader (tilbake til en map med parametre) acc + val}.collectEntries { key, val -> // hent sykdomskode.id if (key.endsWith('.sykdomskode') && val) { [key+'.id', Sykdomskode.findByNr(val)?.id ?: ""] } else { [key, val] }}.sort { it.key}
more...
groovy.codehaus.org
meetup.com/Oslo-Groovy-Meetup
questions ?
Ubrukt
sample data@EqualsAndHashCode @ToString class Person { def name, address, pets }@EqualsAndHashCode @ToString class Address { def street, city }enum Pet { CAT, DOG, BIRD, DUCK }import static Pet.*def persons = [ new Person(name:"Ole", address:new Address(street:"Blindvn 1", city:"Oslo"), pets:[BIRD, CAT]), new Person(name:"Dole", address:new Address(street:"Blindvn 2", city:"Oslo"), pets:[DOG, CAT]), new Person(name:"Doff", address:new Address(street:"Strandvn 9", city:"Bergen"), pets:[BIRD, DOG]), ]assert persons.pets == [ [BIRD, CAT], [DOG, CAT], [BIRD, DOG]] assert persons.pets.flatten().unique() == [BIRD, CAT, DOG]assert persons.address.city.unique() == ["Oslo", "Bergen"]
real-world exampleimport org.ccil.cowan.tagsoup.Parser@Grab(group='org.ccil.cowan.tagsoup', module='tagsoup', version='0.9.7')
def gdocHome = "http://groovy.codehaus.org/groovy-jdk/"def gdocs = ["java/lang": ["Object[]","Object"], "java/util": ["Collection", "List", "Map"]]
gdocs.inject([:]) { agg, key, val -> // create map of [class:url] agg + val.collectEntries { [it, gdocHome + key+"/"+it+".html" ] }}.collectEntries { className, url -> // parse urls def html = new XmlParser(new Parser()).parse(url) [className, html.body.table[1].tr[1..-1].collect { it.td[1].code.b.a.text() }]}
• gå dypt i utvalgte kall, ex
• groupBy
• inject
• findResult: find + transform
• nested collections: collectNested
Groovy Collection Jdk Doc
• Object
• Object[]
• Collection
• Map
• List
• Set
Basic collection types
assert [] instanceof ArrayList // (java.util)assert [:] instanceof LinkedHashMap // predictable order assert ([] as Set) instanceof HashSetassert (1..3) instanceof IntRange // groovy.langassert (1..3) instanceof List