SlideShare a Scribd company logo
Groovy Collection API
Trygve Amundsen, Kodemaker
basics
groovy collections
•
•
•
•
•
•
•

Java Collection (List, Map, Set mfl.)
Array
String
Regexp
Iterable
CharSequence
Object!

assert "string".collect { it } == ['s','t','r','i','n','g']
assert 42.collect { it } == [42]

// string
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.lang
assert (1..3) instanceof List
notation, list operators
assert
assert
assert
assert

[1,2] << 3 == [1,2,3]
// leftShift()
[1,2]+3 == [1,2,3]
// plus()
[1,2]+[3,4] == [1,2,3,4]
[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]
method returns
item content
collect
collectEntries
collectNested

Object

sort
groupBy

findAll
unique
tail
drop/take

stack

push/pop

max, min, sum, count
join
inject

Collection

multiple items

one item

each
eachWithIndex
reverseEach
find
head
first/last

changed

countBy
findResults
collectMany
split

plus
minus
intersect

subsequences
permutations
combinations

set

boolean

unchanged
changed

number of items

unchanged

any
every
reducers
assert [1,2,3].max() == 3
assert [1,2,3].min() == 1
assert [1,2,3].sum() == 6
assert [1,2,3].max{ -it } == 1
assert [1,2,3].max{ a,b-> b<=>a } == 1
reducers, cont’d
assert [1,2,3].any{ it > 3 } == false
assert [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 } == 6
assert [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 Set
assert [1,2,3].permutations() == [[1, 2, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2], [2, 1, 3], [1, 3, 2]] as Set
assert [[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 style
assert [1,2,3].head() == 1
assert [1,2,3].tail() == [2,3]
assert [1,2,3].first() == 1
assert [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 stack
NOTE: mutates!

def list = [1,2,3]
list.push(4)
assert list == [1,2,3,4]
assert list.pop() == 4
assert list == [1,2,3]
findResults
assert [1,2,3].findResults { it > 1 ? it*it : null } == [4,9]

[1,2,3].findResults { it > 1 ? it*it : null }

is identical to
[1,2,3].findAll { it > 1 }
.collect { it*it }
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 example
import 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
assert
assert
assert
assert

[] instanceof ArrayList
// (java.util)
[:] instanceof LinkedHashMap
// predictable order
([] as Set) instanceof HashSet
(1..3) instanceof IntRange
// groovy.lang
(1..3) instanceof List

More Related Content

Groovy collection api

  • 1. Groovy Collection API Trygve Amundsen, Kodemaker
  • 3. groovy collections • • • • • • • Java Collection (List, Map, Set mfl.) Array String Regexp Iterable CharSequence Object! assert "string".collect { it } == ['s','t','r','i','n','g'] assert 42.collect { it } == [42] // string
  • 4. 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]
  • 5. 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
  • 6. ranges are lists assert (1..3) == [1,2,3] assert (1..<3) == [1,2] assert (1..3) instanceof IntRange // groovy.lang assert (1..3) instanceof List
  • 7. notation, list operators assert assert assert assert [1,2] << 3 == [1,2,3] // leftShift() [1,2]+3 == [1,2,3] // plus() [1,2]+[3,4] == [1,2,3,4] [1,2,3]-[3,4] == [1,2] // minus()
  • 8. notation, spread operator assert ["a","b","1"]*.isNumber() == [false,false,true] assert ["a","b","1"].number == [false,false,true]
  • 10. 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 }
  • 11. 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]
  • 12. 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]
  • 13. method returns item content collect collectEntries collectNested Object sort groupBy findAll unique tail drop/take stack push/pop max, min, sum, count join inject Collection multiple items one item each eachWithIndex reverseEach find head first/last changed countBy findResults collectMany split plus minus intersect subsequences permutations combinations set boolean unchanged changed number of items unchanged any every
  • 14. reducers assert [1,2,3].max() == 3 assert [1,2,3].min() == 1 assert [1,2,3].sum() == 6 assert [1,2,3].max{ -it } == 1 assert [1,2,3].max{ a,b-> b<=>a } == 1
  • 15. reducers, cont’d assert [1,2,3].any{ it > 3 } == false assert [1,2,3].every{ it < 4 } == true assert [1,2,3].join(";") == "1;2;3"
  • 16. reducers, inject assert [1,2,3].inject(0) { acc, val -> acc+val } == 6 assert [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
  • 17. inject -> reduce Object.metaClass.reduce = {initial, closure-> delegate.inject(initial, closure) } [1,2,3].reduce(0){acc,val->acc+val}
  • 19. 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]
  • 20. 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 Set assert [1,2,3].permutations() == [[1, 2, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2], [2, 1, 3], [1, 3, 2]] as Set assert [[1,2],[3,4]].combinations() == [[1, 3], [2, 3], [1, 4], [2, 4]]
  • 21. 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]]
  • 22. functional style assert [1,2,3].head() == 1 assert [1,2,3].tail() == [2,3] assert [1,2,3].first() == 1 assert [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]]
  • 23. list as stack NOTE: mutates! def list = [1,2,3] list.push(4) assert list == [1,2,3,4] assert list.pop() == 4 assert list == [1,2,3]
  • 24. findResults assert [1,2,3].findResults { it > 1 ? it*it : null } == [4,9] [1,2,3].findResults { it > 1 ? it*it : null } is identical to [1,2,3].findAll { it > 1 } .collect { it*it }
  • 25. Other, possibly useful methods assert [1,2,3].collectMany { [it**2, it**3] } == [1,1,4,8,9,27]
  • 26. map withDefault def newMap = [:].withDefault { [] } [a:1,b:2,c:2].each { key, val -> newMap[val] << key } assert newMap == [1:['a'], 2:['b','c']]
  • 27. 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!)
  • 32. 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 }
  • 36. 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"]
  • 37. real-world example import 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() }] }
  • 38. • gå dypt i utvalgte kall, ex • • • • groupBy inject findResult: find + transform nested collections: collectNested
  • 39. Groovy Collection Jdk Doc • Object • Object[] • Collection • Map • List • Set
  • 40. Basic collection types assert assert assert assert assert [] instanceof ArrayList // (java.util) [:] instanceof LinkedHashMap // predictable order ([] as Set) instanceof HashSet (1..3) instanceof IntRange // groovy.lang (1..3) instanceof List

Editor's Notes

  1. merk strings og charsequence
  2. Bra for named parameters!
  3. Some say thats bad, wtf
  4. leftShift == putte inn i - operatoren
  5. Ta litt tid hero - isNumber er en String operasjon (ikke en collection operasjon) - Collections har ikke properties, så variant 2 representerer ikke noen tvetydigheter
  6. Trofast, første møtet Returnerer ingenting (jo, umodifisert collection) Bare en vanlig for-loop, en blindvei på veien mot funksjonell nirvana Avhengig av side-effekter for å utføre noe.
  7. find burde kanskje hete findFirst burde vært en alias for filter - det er det ikke
  8. Heter map i mange språk, men collect passer godt i en java-verden map henviser jo til noe helt annet Veldig nyttig: hvis man vil ha map: bruk collectEntries, returner en array [key, val] tygg på navnet: godt navn
  9. Fra 30000 fot: bilde!!
  10. Reducers - noen kaller det foldLeft, silly :) (alle disse tar også transformer/comparator for ikke-standard sammenligning)
  11. Dette er lett ;) Burde hett reduce. foldLeft i mange språk. foldRight Mangler i groovy. Ta litt tid: 1) initial value 2) to understand: closure returns next accumulator
  12. Bare mer komplisert. Hva gjør den nullen der? (indikerer at raden skal filtreres bort - ikke akkurat selvforklarende) Jeg nevner dette for det ikke står i doken
  13. Her er de fleste collection-metoder (+ noen fler) definert resten er i StringGroovyMethods