working with groovy collections
TRANSCRIPT
Ted VinkeFirst8 September 2015
COLLECTIONSgroovyCollections.each { “working with $it” }
Overview• Java Collections Basics
• Iterable & Collection• Groovy Makes Things Easy
• List, Set, Range, Map• GDK’s Iterable & Collection
• Some examples• collate, collect, collectEntries, every and any, find,
findResults, groupBy, inject, removeAll, sum• References
JAVA COLLECTIONS BASICSWhere it all starts
Java Collections
Iterable interface• iterator
Iterator interface• hasNext• next• remove
Collection interface• add• addAll• clear• contains(Object)• containsAll(Collection)• isEmpty• iterator• remove• removeAll• retainAll• size• toArray
GROOVY MAKES THINGS EASY
Groovy Makes Things Easy• Creating a collection
• list = new ArrayList() vs list = []• map = new HashMap() vs map = [:]
• Working with elements• Addition e.g. list.add(3) vs list << 3• Retrieval e.g. map.get('color') vs map.color
• Overloading• list1.addAll(list2) vs list1 + list2
Listdef list = ['a', 'b']list << 'c'assert list.size() == 3assert list.get(0) == 'a'assert list instanceof java.util.List
def emptyList = []assert emptyList.size() == 0
List is an ordered collection of elements
Setdef list = ['a', 'b']list << 'c'list << 'c'assert list.size() == 4
def set = list as Setassert set.size() == 3assert set instanceof java.util.Set
Set is a collection with no duplicates
Rangedef range = 5..8assert range.size() == 4assert range[2] == 7assert range instanceof java.util.List
range = 'a'..'d'assert range.size() == 4assert range[2] == 'c'assert range.from == 'a'assert range.to == 'd'
Range is an extension of List
Mapdef map = [name: 'Ted', age: 34]map.color = 'blue'assert map.size() == 3assert map['name'] == 'Ted'assert map instanceof java.util.Map
def emptyMap = [:]assert emptyMap.size() == 0
Map maps keys to values – e.g. a dictionary.
No duplicate keys.
Tip: def“The keyword def is
great for examples, but not always so much for
real API design.”
IT’S NOT ONLYSYNTACTIC SUGAR
addAll, addAll, addAll, asBoolean, asImmutable, asSynchronized, asType, collect, collect, collect, collectNested, each, eachWithIndex, find, find, findAll, findAll, findResult,findResult, flatten, getAt, getIndices, grep, grep, inject, inject, intersect, isCase, leftShift, minus, plus, plus, plus, removeAll, removeAll, removeElement, retainAll, retainAll,split, toListString, toListString, toSet, unique, unique, unique, unique, unique, unique Groovy’s Collection
any, asCollection, asList, collate, collate, collate, collate, collectEntries, collectEntries, collectEntries, collectEntries, collectMany, collectMany, collectNested,collectNested, combinations, combinations, contains, containsAll, count, count, countBy, disjoint, drop, dropRight, dropWhile, each, eachCombination, eachPermutation,eachWithIndex, every, findResults, first, flatten, flatten, getAt, groupBy, groupBy, groupBy, head, indexed, indexed, init, intersect, join, last, max, max, max, min, min, min, minus,minus, multiply, permutations, permutations, plus, plus, size, sort, sort, sort, sort, sort, sum, sum, sum, sum, tail, take, takeRight, takeWhile, toList, toSet, toSorted, toSorted,toSorted, toSpreadMap, toUnique, toUnique, toUnique, withIndex, withIndex
Groovy’s Iterable
SOME EXAMPLES
collatedef names = ["Lassy", "Buttercup", "Carmella", "Moo-moo", "Cinnamon"]
// collate into batches of 3def collated = names.collate 3assert collated == [['Lassy', 'Buttercup', 'Carmella'], ['Moo-moo', 'Cinnamon']]
// and passing a step of 1collated = names.collate 3, 1assert collated == [['Lassy', 'Buttercup', 'Carmella'], ['Buttercup', 'Carmella', 'Moo-moo'], ['Carmella', 'Moo-moo', 'Cinnamon'], ['Moo-moo', 'Cinnamon'], ['Cinnamon']]
Remember closures?
def adder = { a, b -> print a+b }assert adder(1, 2) == 3assert adder("m", "e") == "me"
Closures are used a lot!• e.g. basic looping
• dogs.each { it.bark() }• names.each { name ->
println “Hello $name”}
• or whatever the methods’ Javadoc says
collectdef animals = []animals << new Animal(name: "Buttercup", age: 2)animals << new Animal(name: "Carmella", age: 5)animals << new Animal(name: "Cinnamon", age: 2)
// collect on Listdef names = animals.collect { it.name }assert names == ["Buttercup", "Carmella", "Cinnamon"] // names is java.util.ArrayList
// collect as a different type e.g. a Set, keeping only unique valuesassert animals.collect(new HashSet()) { it.age }.toString() == "[2, 5]"
collectEntriesdef john = new Farmer(name: "John")def dick = new Farmer(name: "Dick")
def animals = []animals << new Animal(name: "Buttercup", farmer: john)animals << new Animal(name: "Carmella", farmer: dick)animals << new Animal(name: "Cinnamon", farmer: dick)
// collect farmer name by animal namedef results = animals.collectEntries { animal -> [animal.name, animal.farmer.name]}assert results == ['Buttercup':'John', 'Carmella':'Dick', 'Cinnamon':'Dick']
every and anydef animals = []animals << new Animal(name: "Buttercup", age: 2)animals << new Animal(name: "Carmella", age: 5)animals << new Animal(name: "Cinnamon", age: 2)
// check if all animals are youngboolean allAreYoung = animals.every { it.age < 3 } assert !allAreYoung // Carmella is, with age 5, not young
// check if atleast one of them is youngboolean atleastOneIsYoung = animals.any { it.age < 3 }assert atleastOneIsYoung // Buttercup and Cinnamon are both young enough
finddef animals = []animals << new Animal(name: "Buttercup", age: 2)animals << new Animal(name: "Carmella", age: 5)animals << new Animal(name: "Cinnamon", age: 2)
// find first matching animal Animal foundAnimal = animals.find { it.age == 2 }assert foundAnimal.name == "Buttercup"
// find all matching animalsList foundAnimals = animals.findAll { it.name.startsWith "C" } assert foundAnimals.toString() == "[Carmella, Cinnamon]"
findResultsdef animals = []animals << new Animal(name: "Buttercup", age: 2)animals << new Animal(name: "Carmella", age: 5)animals << new Animal(name: "Cinnamon", age: 2)
// find all non-null resultsdef greetings = animals.findResults { it.age < 4 ? "Hello $it.name!" : null} assert greetings == ['Hello Buttercup!', 'Hello Cinnamon!']
groupBydef animals = []animals << new Animal(name: "Buttercup", country: "NL", age: 2)animals << new Animal(name: "Carmella", country: "NL", age: 5)animals << new Animal(name: "Cinnamon", country: "BE", age: 2)
// single closureMap animalsByCountry = animals.groupBy { it.country }assert animalsByCountry["NL"].size() == 2 // Buttercup and Carmellaassert animalsByCountry.BE.size() == 1 // Cinnamon
// list of closuresMap animalsByCountryAndAge = animals.groupBy( [{ it.country }, { it.age }] ) assert animalsByCountryAndAge.toString() == "[NL:[2:[Buttercup], 5:[Carmella]], BE:[2:[Cinnamon]]]"
injectdef animals = []animals << new Animal(name: "Buttercup", price: 2, farmer: "john")animals << new Animal(name: "Carmella", price: 5, farmer: "dick")animals << new Animal(name: "Cinnamon", price: 2, farmer: "dick")
// iteration, passing 1st item to closure, injecting results back into closure with 2nd item, etcassert 1 * 2 * 3 * 4 == [1, 2, 3, 4].inject { current, val -> current * val }
// gather the total price of all animals, passing initial price of 0assert 9 == animals.inject(0) { totalPrice, animal -> totalPrice + animal.price }// or...assert 9 == animals.price.inject(0) { totalPrice, price -> totalPrice + price}
removeAlldef animals = []animals << new Animal(name: "Buttercup", age: 2)animals << new Animal(name: "Carmella", age: 5)animals << new Animal(name: "Cinnamon", age: 2)
// remove all animals younger than 3animals.removeAll { it.age < 3 }assert animals.size() == 1assert animals.first().name == "Carmella"
sumdef animals = []animals << new Animal(name: "Buttercup", price: 2, farmer: "john")animals << new Animal(name: "Carmella", price: 5, farmer: "dick")animals << new Animal(name: "Cinnamon", price: 2, farmer: "dick")
// gather the total price of all animalsassert 9 == animals.sum { it.price }assert 9 == animals.price.sum()
MAKING COMBINATIONS
Animals by farmer// groupingdef animalsByFarmer =
animals.groupBy { it.farmer }
assert "[john:[Buttercup], dick:[Carmella, Cinnamon]]" == animalsByFarmer.toString()
Total price per farmer// grouping, collecting and summingdef totalPriceByFarmer = animals .groupBy { it.farmer } .collectEntries { k, v -> [k, v.price.sum()] }assert ['john':2, 'dick':7] == totalPriceByFarmer
Tip: sort mutatesdef animalList = createAnimals([])assert animalList instanceof java.util.ArrayListassert animalList.name == ["Lassy", "Buttercup", "Moo-moo"]
List sortedByAge = animalList.sort { it.age }assert animalList.name == ["Moo-moo", "Lassy", "Buttercup"]assert sortedByAge.name == ["Moo-moo", "Lassy", "Buttercup"]assert animalList.is(sortedByAge) // is() is same as == in plain Java
def animalSet = createAnimals([] as Set)assert animalSet instanceof java.util.HashSet
List sortedByName = animalSet.sort { it.name }assert sortedByName.name == ["Buttercup", "Lassy", "Moo-moo"]assert animalSet != sortedByNameassert !animalSet.is(sortedByName)
List againAnimalList = createAnimals([])// pass false as the 1st parameter to indicate NOT to mutate original ListList againSortedByAge = againAnimalList.sort(false) { it.age }assert againAnimalList.name == ["Lassy", "Buttercup", "Moo-moo"]assert againSortedByAge.name == ["Moo-moo", "Lassy", "Buttercup"]
def createAnimals = { c -> c << new Animal(name: "Lassy", age: 2) c << new Animal(name: "Buttercup", age: 3) c << new Animal(name: "Moo-moo", age: 1) c}
References• Oracle Java™ Tutorials: Collections Trailhttps://docs.oracle.com/javase/tutorial/collections/index.html
• The Groovy Dev Kit: Working with Collectionshttp://groovy-lang.org/groovy-dev-kit.html#_working_with_collections
• Groovy Closureshttp://groovy-lang.org/closures.html