You are on page 1of 22

Groovy Goodness Notebook

Hubert A. Klein Ikkink (mrhaki)


This book is for sale at http://leanpub.com/groovy-goodness-notebook
This version was published on 2013-12-12

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing process.
Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and many
iterations to get reader feedback, pivot until you have the right book and build traction once you do.
2012 - 2013 Hubert A. Klein Ikkink (mrhaki)

Tweet This Book!


Please help Hubert A. Klein Ikkink (mrhaki) by spreading the word about this book on Twitter!
The suggested tweet for this book is:
I just bought Groovy Goodness Notebook with Groovy Goodness blog posts bundled into one book.
#groovy @mrhaki
The suggested hashtag for this book is #groovygoodnessnotebook.
Find out what other people are saying about the book by clicking on this link to search for this hashtag
on Twitter:
https://twitter.com/search?q=#groovygoodnessnotebook

This book is dedicated to my lovely family. I love you.

Contents
Dates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Working with Dates . . . . . . . . . . . . . . . . . . . . . . . . . .
Setting Date and Calendar Values with Subscript Operators . . . . .
Convert Date to java.sql.Timestamp . . . . . . . . . . . . . . . . . .
Convert Date to Calendar . . . . . . . . . . . . . . . . . . . . . . .
Format Dates with TimeZone . . . . . . . . . . . . . . . . . . . . .
Parse Date.toString() Value . . . . . . . . . . . . . . . . . . . . . . .
Use the set Method to Define a Date or Calendar Value . . . . . . .
Create New Date from Old Date with Updates . . . . . . . . . . . .
Create New Date or Calendar from Existing and Set Property Value
Clear Time Portion of a Date . . . . . . . . . . . . . . . . . . . . . .
Date and Time Durations and the TimeCategory . . . . . . . . . . .
Loop Through Date and Calendar Ranges . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

1
1
2
2
3
3
4
5
5
6
6
7
8

Maps . . . . . . . . . . . . . . . . . . . . . .
Check if Maps are Equal . . . . . . . . . .
Sorting a Map . . . . . . . . . . . . . . . .
Turn a List into a Map . . . . . . . . . . .
Complex Keys in Maps . . . . . . . . . . .
Use inject Method on a Map . . . . . . . .
Intersect Maps . . . . . . . . . . . . . . . .
Subtracting Map Entries . . . . . . . . . .
Process Map Entries in Reverse . . . . . .
Getting a Submap from a Map . . . . . . .
Grouping Map Elements . . . . . . . . . .
Get Value from Map or a Default Value . .
Map with Default Values . . . . . . . . . .
Determine Min and Max Entries in a Map

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

10
10
10
11
11
12
12
12
13
13
14
15
16
16

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

Dates
Working with Dates
Thanks to Groovys extensions to the JDK Date classes we can work with dates more easily. For example
we can now add and subtract days with the plus() and minus() methods. And because this methods are
mapped to the operators + and - we can write dense code.
Also the next() and previous() methods are implemented so we can use the ++ and -- operators to get
to a next or previous day. We can even use the subscript operator ([]) to get date fields, because of the
getAt() method Groovy adds to the Date class.
The Date class also contains parse() and format() methods to convert string to a date and to format a
date into a string.
// Date.parse() to convert String to Date.
date = new Date().parse('yyyy/MM/dd', '1973/07/09')
// We can use [] or getAt() to get date fields.
assert 1973 == date[Calendar.YEAR]
assert 6 == date[Calendar.MONTH]
assert 9 == date.getAt(Calendar.DATE)
dateNext = date.clone()
datePrevious = date.clone()
// We can use the + and - operators to add or
// subtract days.
nextDay = date + 1 // Or date.plus(1)
previousDay = date - 1 // Or date.minus(1)
// ++ operator to move one day ahead.
dateNext++ // Or dateNext.next()
assert dateNext == nextDay
// -- operator to move one day back.
datePrevious-- // Or datePrevious.previous()
assert datePrevious == previousDay
otherDate = new Date().parse('yyyy/MM/dd', '1973/07/21')
// Dates can be used in ranges.
assert 12 == (otherDate..<date).size()
// Set Locale to assert date formatting.
Locale.setDefault(Locale.US)

Dates

// Date.format() uses java.text.SimpleDateFormat.


assert '9 July, 1973' == date.format("d MMMM, yyyy")
assert '7/9/73' == date.getDateString()

Original blog post written on August 31, 2009

Setting Date and Calendar Values with Subscript Operators


In Groovy 1.7.3 we have some new ways to set the value of Date or Calendar objects. We can for example
use the subscript operators (getAt() and putAt() methods) to define values for fields or get the value
from a field. The fields are the Calendar constants like Calendar.YEAR, Calendar.DATE.
import static java.util.Calendar.*
def date = new Date()
// Set value with subscript operator
date[YEAR] = 2010
date[MONTH] = JUNE
date[DATE] = 14
assert 110 == date.year
assert 5 == date.month
assert 14 == date.date
// Get
assert
assert
assert

value with subscript operator


2010 == date[YEAR]
JUNE == date[MONTH]
14 == date[DATE]

def cal = Calendar.instance


// Set value with subscript operator
cal[YEAR] = 2000
cal[MONTH] = DECEMBER
cal[DATE] = 25
assert '2000-12-25' == cal.format('yyyy-MM-dd')
assert 2000 == cal[YEAR] // Get value with subscript operator

Original blog post written on June 14, 2010.

Convert Date to java.sql.Timestamp


Groovy adds many methods to standard Java classes we can use in our code. To convert a simple Date
object to a java.sql.Timestamp we can simply use the toTimestamp() method on a Date object.

Dates

import static java.util.Calendar.*


// Create date object with specific year, month and day.
def date = new Date()
date.clearTime()
date.set year: 2010, month: AUGUST, date: 10
// Convert to java.sql.Timestamp.
def sqlTimestamp = date.toTimestamp()
assert 'java.sql.Timestamp' == sqlTimestamp.class.name
assert '2010-08-10 00:00:00.0' == sqlTimestamp.toString()

Original blog post written on August 30, 2010.

Convert Date to Calendar


In Groovy version 1.7.6 we can convert a Date to a Calendar with the toCalendar() method. The
toCalendar() method is added to the Date class by Groovy.
import static java.util.Calendar.*
def date = new Date()
date.set year: 2010, month: 11, date: 16
def calendar = date.toCalendar()
assert
assert
assert
assert
assert

calendar[YEAR] == 2010
calendar[MONTH] == Calendar.DECEMBER
calendar[DATE] == 16
calendar.format('dd-MM-yyyy') == '16-12-2010'
calendar in Calendar

Original blog post written on December 16, 2010.

Format Dates with TimeZone


Since Groovy 1.8.3 we can use an extra TimeZone parameter with the format() method of the Date class.
This can be used to print a date/time for a particular timezone.

Dates

import static java.util.Calendar.*


def timeZone = TimeZone.getTimeZone('Europe/Amsterdam')
def otherTimeZone = TimeZone.getTimeZone('Australia/Canberra')
def cal = Calendar.instance
cal.set(year: 2011, month: OCTOBER, date: 20, hourOfDay: 12, minute: 30)
def date = cal.time
def dateFormat = 'yyyy/MM/dd HH:mm'
assert date.format(dateFormat, timeZone) == '2011/10/20 12:30'
assert date.format(dateFormat, otherTimeZone) == '2011/10/20 21:30'

Original blog post written on October 20, 2011.

Parse Date.toString() Value


With Groovy 1.8.4 we can parse the output of the Date.toString() method back to a Date with the
parseToStringDate() method. For example we get the String value of a Date from an external source
and want to parse it to a Date object. The format of the String must have the pattern EEE MMM dd
HH:mm:ss zzz yyyy with the US Locale. This is used by the toString() method of the Date class.
import static java.util.Calendar.*
// Create date 10 November 2011.
def cal = Calendar.getInstance(TimeZone.getTimeZone('Europe/Amsterdam'))
def date = cal.time
date.clearTime()
date[YEAR] = 2011
date[MONTH] = NOVEMBER
date[DATE] = 10
// Get toString() value.
def dateToString = date.toString()
assert dateToString == 'Thu Nov 10 00:00:00 CET 2011'
// Replace Nov for Dec in string and 10 for 24.
dateString = dateToString.replace('Nov', 'Dec').replace('10', '24')
// Use parseToStringDate to get new Date.
def newDate = Date.parseToStringDate(dateString)
assert newDate[MONTH] == DECEMBER
assert newDate[DATE] == 24
assert newDate[YEAR] == 2011

Original blog post written on November 10, 2011.

Dates

Use the set Method to Define a Date or Calendar Value


In a previous post we learned about the new subscript operator support in Groovy 1.7.3 for setting Date
or Calendar values. But we have other new ways in Groovy 1.7.3: we can use a set() method to set the
values. The method accepts a Map with the following keys: year, month, date, hourOfDay, minute and
second. The keys are used to set the according values of the Date or Calendar object.
import static java.util.Calendar.*
def cal = Calendar.instance
cal.set(year: 2010, month: JULY, date: 9)
assert 'Birthday @ 2010-7-9' == 'Birthday @ ' + cal.format('yyyy-M-d')
assert FRIDAY == cal[DAY_OF_WEEK]
def date = new Date()
date.set(hourOfDay: 12, minute: 0, second: 0, year: 2010, month: JUNE, date: 1)
assert
assert
assert
assert

'12:00:00' == date.format('HH:mm:ss')
2010 == date[YEAR]
JUNE == date[MONTH]
1 == date.getAt(DATE)

Original blog post written on June 14, 2010.

Create New Date from Old Date with Updates


Groovy 1.7.3 adds the updated() method to the Date and Calendar classes. We pass a Map as parameter
to this method to define which fields are updated. The fields that are not mentioned are unchanged. The
result is a new Date or Calendar object, our original object stays the same.
import static java.util.Calendar.*
def cal = Calendar.instance
cal[MONTH] = DECEMBER
def calNextMonth = cal.updated(month: cal[MONTH] + 1, year: 2012)
assert JANUARY == calNextMonth[MONTH]
assert 2013 == calNextMonth[YEAR]
def date = new Date()
date.set(year: 2011, month: MAY, date: 5)
def dateTenYearsAgo = date.updated(year: date[YEAR] - 10)
assert '2011-5-5' == date.format('yyyy-M-d')
assert '2001-5-5' == dateTenYearsAgo.format('yyyy-M-d')

Original blog post written on June 14, 2010.

Dates

Create New Date or Calendar from Existing and Set Property Value
Since Groovy 2.2 we can create a new Date or Calendar object from an existing Date and Calendar
object and directly change property values. We must use the copyWith() method and we pass a map
with property names and values as an argument. The newly created object will have the old values for
properties from the original object and the properties set in the map are overridden.
The following code shows the new copyWith() method:
import static java.util.Calendar.NOVEMBER
// Create original date.
def date = new Date().clearTime()
date.set(year: 2013, month: NOVEMBER, date: 18)
// Use copyWith to get new Date and
// immmediatelly set year to 2014.
def yearLater = date.copyWith(year: 2014)
assert yearLater.format('dd-MM-yyyy') == '18-11-2014'

// Also works for Calendar.


def cal = Calendar.instance
cal.set(year: 2013, month: NOVEMBER, date: 10)
// Create new Calendar with new date value.
def newCalendar = cal.copyWith(date: 18)
assert newCalendar.format('dd-MM-yyyy') == '18-11-2013'

Code written with Groovy 2.2.


Original blog post written on November 18, 2013.

Clear Time Portion of a Date


Working with dates in Groovy is easy. We get a lot of extra functionality compared to the standard Java
Date class. One of the extra methods added to the Date class since Groovy 1.6.8 is clearTime(). With
clearTime() we reset the time portion of a date to 12 oclock midnight. This makes it easier to compare
dates if we only are interested in the date, month, year parts.

Dates

// Create new date.


def d = new Date(year: 2010, month: Calendar.JULY, date: 1,
hours: 7, minutes: 12, seconds: 0)
assert '7/1/10 7:12:00 AM' == d.dateTimeString
// Reset time portion of the date.
d.clearTime()
assert '7/1/10 12:00:00 AM' == d.dateTimeString

Original blog post written on July 1, 2010.

Date and Time Durations and the TimeCategory


Groovy has some elegant ways to work with date and time values. One of them is the support of durations.
We can define a duration to denote a certain time amount, like 7 days, 2 hours and 50 minutes. We can
use these durations to add or subtract them from date and time objects.
The TimeCategory provides an even Groovier way to work with durations. We can use constructs like
7.days + 12.minutes to create a duration. When we read this code it is just like reading English text.
Here is some sample code:
import groovy.time.*
// Define period of 2 years, 3 months, 15 days, 0 hours,
// 23 minutes, 2 seconds and 0 milliseconds.
def period = new DatumDependentDuration(2, 3, 15, 0, 23, 2, 0)
assert '2 years, 3 months, 15 days, 23 minutes, 2.000 seconds' ==
period.toString()
def year2000 = new Date(100, 0, 0) // Jan 1, 2000
assert '14 Apr 2002 22:23:02 GMT' == (period + year2000).toGMTString()
// Define time period of 5 hours, 54 minutes and 30 milliseconds.
def time = new TimeDuration(5, 54, 0, 30)
assert '5 hours, 54 minutes, 0.030 seconds' == time.toString()
use (TimeCategory) {
assert period.toString() == (2.years + 3.months + 15.days +
0.hour + 23.minutes + 2.seconds)
.toString()
assert time.toString() == (5.hours + 54.minutes + 30.milliseconds)
.toString()
// We can use period.from.now syntax.
def d1 = 1.week - 1.day
def d2 = new Date() + 6.days

Dates

assert d2.format('yyyy-MM-dd') == d1.from.now.toString()


// We can use period.ago syntax.
def d3 = 3.days.ago
def d4 = new Date() - 3
assert d4.format('yyyy-MM-dd') == d3.toString()
}

Original blog post written on September 2, 2009

Loop Through Date and Calendar Ranges


Groovy already adds a lot of extra methods to the Date and Calendar classes. And since Groovy 2.2 we can
use the upto() method and loop over a begin date up to another date. We pass a closure that is executed
for each day. We can also iterate back using the downto() method.
// We can loop from today
// to nextWeek and invoke a closure for each day.
def today = new Date().clearTime()
def nextWeek = today + 7
today.upto(nextWeek) {
// Print day of the week.
println it.format('EEEE')
}
println()
nextweek.downto(today) {
prinltn it.format('EEEE')
}

When we run the code we get the following output:


Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Monday
Monday
Sunday
Saturday
Friday

Dates

Thursday
Wednesday
Tuesday
Monday

In the next sample we use the upto() method on the Calendar class:
// upto() also works on Calendar objects.
def to = Calendar.instance
to.set(year: 2013, month: Calendar.NOVEMBER, date: 18)
def from = Calendar.instance
from.set(year: 2013, month: Calendar.NOVEMBER, date: 13)
from.upto(to) {
if (it[Calendar.DATE] % 2 == 0) {
print 'Even'
} else {
print 'Odd'
}
println ' date'
}

We get the following output:


Odd date
Even date
Odd date
Even date
Odd date
Even date

Code written with Groovy 2.2.


Original blog post written on November 18, 2013.

Maps
Check if Maps are Equal
With Groovy 1.8 the equals() method is added to Map. This means we can check if maps are equals. They
are equals if both maps have the same size, and keys and values are the same.
def map1 = [user: 'mrhaki', likes: 'Groovy', age: 37]
def map2 = [age: 37.0, likes: 'Groovy', user: 'mrhaki']
def map3 = [user: 'Hubert Klein Ikkink', likes: 'Groovy']
assert
assert
assert
assert

map1.equals(map2)
map1 == map2
!map1.equals(map3)
map2 != map3

Original blog post written on April 27, 2011.

Sorting a Map
Maps dont have an order for the elements, but we may want to sort the entries in the map. Since Groovy
1.7.2 we can use the sort() method which uses the natural ordering of the keys to sort the entries. Or we
can pass a Comparator to the sort() method to define our own sorting algorithm for the keys.
def m = [sort: 'asc', name: 'test', paginate: true, max: 100]
def expectedKeys = ['max', 'name', 'paginate', 'sort']
// Since 1.7.2
assert expectedKeys == m.sort()*.key
// Since 1.7.2
assert expectedKeys == m.sort( { k1, k2 -> k1 <=> k2 } as Comparator )*.key
// Sorting before Groovy 1.7.2
assert expectedKeys == new TreeMap(m)*.key
// Sort by closure.
assert expectedKeys == m.sort { e1, e2 -> e1.key <=> e2.key }*.key

Original blog post written on April 20, 2010.

11

Maps

Turn a List into a Map


With Groovy we can use the values of an Object array and transform them to a map with the
toSpreadMap() method. The array must have an even number of elements, because the odd elements
are the keys for the new map and the even numbers are the values for the keys. The SpreadMap object,
which now contains the keys and values, is an immutable map, so we cannot change the contents once
we have created the map.
def list = ['key', 'value', 'name', 'mrhaki'] as Object[]
def map = list.toSpreadMap()
assert 2 == map.size()
assert 'value' == map.key
assert 'mrhaki' == map['name']

Original blog post written on January 4, 2010.

Complex Keys in Maps


In Groovy we can use non-string keys for maps. We only have to place parenthesis around the key to
make it work. This way we can use variables and types like Date and Boolean as keys for our map. When
we use parenthesis around the key when using the . notation the key is converted to a String, otherwise
the key is not converted and keeps it type.
def key = 100

// Variable to be used a key.

def m = [
(new Date(109, 11, 1)): 'date key',
(-42): 'negative number key',
(false): 'boolean key',
(key): 'variable key'
]
m.(true) = 'boolean key' // Key is converted to String.
m.(2 + 2) = 'number key'
m[(key + 1)] = 'number key' // Key keeps to be Integer.
assert
assert
assert
assert
assert
// Key
assert
assert
assert

'date key' == m[new Date(109, 11, 1)]


'negative number key' == m.get(-42)
'boolean key' == m[(false)]
'variable key' == m[100]
'variable key' == m.getAt(key)
is String so we can use it to get the value.
'boolean key' == m['true']
'number key' == m.'4'
'number key' == m.get(101)

Original blog post written on November 7, 2009

Maps

12

Use inject Method on a Map


The inject() method is since Groovy 1.8.1 also available for Map objects. The closure arguments
accepts two or three arguments. With the three-argument variant we get the key and value separately
as arguments. Otherwise we get a map entry as closure argument.
// 3-argument closure with key, value.
def m = [user: 'mrhaki', likes: 'Groovy']
def sentence = m.inject('Message: ') { s, k, v ->
s += "${k == 'likes' ? 'loves' : k} $v "
}
assert sentence.trim() == 'Message: user mrhaki loves Groovy'
// 2-argument closure with entry.
def map = [sort: 'name', order: 'desc']
def equalSizeKeyValue = map.inject([]) { list, entry ->
list << (entry.key.size() == entry.value.size())
}
assert equalSizeKeyValue == [true, false]

Original blog post written on September 27, 2011.

Intersect Maps
Since Groovy 1.7.4 we can intersect two maps and get a resulting map with only the entries found in both
maps.
def m1 = [a: 'Groovy', b: 'rocks', c: '!']
def m2 = [a: 'Groovy', b: 'rocks', c: '?', d: 'Yes!']
assert [a: 'Groovy', b: 'rocks'] == m1.intersect(m2)
assert [1: 1.0, 2: 2] == [1: 1.0, 2: 2].intersect([1: 1, 2: 2.0])

Original blog post written on August 9, 2010.

Subtracting Map Entries


Groovy 1.7.4 adds the minus() method to the Map class. The result is a new map with the entries of the
map minus the same entries from the second map.

Maps

13

def m1 = [user: 'mrhaki', age: 37]


def m2 = [user: 'mrhaki', name: 'Hubert']
def m3 = [user: 'Hubert', age: 37]
assert [age: 37] == m1 - m2
assert [user: 'mrhaki'] == m1 - m3

Original blog post written on August 9, 2010.

Process Map Entries in Reverse


Since Groovy 1.7.2 we can loop through a Map in reverse with the reverseEach(). The order in which the
content is processed is not guaranteed with a Map. If we use a TreeMap the natural ordering of the keys of
the map is used.
def reversed = [:]
[a: 1, c: 3, b: 2].reverseEach { key, value ->
reversed[key] = value ** 2
}
assert [b: 4, c: 9, a: 1] == reversed
// TreeMap uses natural ordering of keys, so
// reverseEach starts with key 'c'.
def tree = [a: 10, c: 30, b: 20] as TreeMap
def reversedMap = [:]
tree.reverseEach {
reversedMap[it.key] = it.value * 2
}
assert [c: 60, b: 40, a: 20] == reversedMap

Original blog post written on August 24, 2010.

Getting a Submap from a Map


To get only a subset of a map we can use the subMap() method. We provide a list of keys as parameter to
define which elements from the map we want returned.

Maps

14

def map = [name: 'mrhaki', country: 'The Netherlands',


blog: true, languages: ['Groovy', 'Java']]
def keys = ['name', 'blog']
assert [name: 'mrhaki', blog: true] == map.subMap(keys)
def booleanKeys = map.findAll { it.value instanceof Boolean }
.collect { it.key }
assert [blog: true] == map.subMap(booleanKeys)
def words = ['a': 'Apple', 'j': 'Java', 'g': 'Groovy', 'c': 'Cool']
def range = 'c'..'h' // Range is also a list and can be used here.
def rangeWords = words.subMap(range).findAll{ it.value }
// words.subMap(range) returns [c:Cool, d:null, e:null,
// f:null, g:Groovy, h:null]
// so we use the findAll method to filter out all null values.
assert ['c': 'Cool', 'g': 'Groovy'] == rangeWords

Original blog post written on October 29, 2009

Grouping Map Elements


In a previous post we learned how to use the groupBy() method on collections. The Map class has an
extra method: groupEntriesBy(). We must provide a closure for this method to define how we want the
elements of the map to be grouped. The result is a new Map with keys and a list of Map$Entry objects for
each key. This is different from the result of the groupBy() method. Because then we get a Map with keys
and a Map for each key.
// A simple map.
def m = [q1: 'Groovy', sort: 'desc', q2: 'Grails']
// Closure we use to define the grouping.
// We want all keys starting with 'q' grouped together
// with the key 'params', all other keys are not grouped.
def groupIt = { key, value ->
if (key.startsWith('q')) {
'params'
} else {
key
}
}
// Use groupEntriesBy.
def groupEntries = m.groupEntriesBy(groupIt)
assert 2 == groupEntries.size()
assert groupEntries.params
assert groupEntries.sort

15

Maps

// Key
assert
assert
assert
assert
assert
assert
assert

for a list of Map$Entry objects.


'desc' == groupEntries.sort[0].value
2 == groupEntries.params.size()
'Groovy' == groupEntries.params[0].value
'q1' == groupEntries.params[0].key
'Grails' == groupEntries.params.find { it.key == 'q2' }.value
groupEntries.params instanceof ArrayList
groupEntries.params[0] instanceof Map$Entry

// Use groupBy.
def group = m.groupBy(groupIt)
assert 2 == group.size()
assert group.params
assert group.sort
// Key for Map with key/value pairs.
assert 'desc' == group.sort.sort
assert 2 == group.params.size()
assert 'Groovy' == group.params.q1
assert 'q1' == group.params.keySet().toArray()[0]
assert 'Grails' == group.params.q2
assert group.params instanceof Map
assert group.params.q1 instanceof String

Original blog post written on October 14, 2009

Get Value from Map or a Default Value


The get() method in the Groovy enhanced Map interface accepts two parameters. The first parameter is
the name of the key we want to get a value for. And the second parameter is the default value if there is
no value for the key.
// Simple map.
def m = [name: 'mrhaki', language: 'Groovy']
assert 'mrhaki' == m.getAt('name')
assert 'mrhaki' == m['name']
assert 'Groovy' == m.language
assert 'mrhaki' == m."name"
assert 'mrhaki' == m.get('name') // We can omit the default value if we know the key e\
xists.
assert 'Groovy' == m.get('language', 'Java')
assert null == m.get('expression') // Non-existing key in map.
assert 'rocks' == m.get('expression', 'rocks') // Use default value, this also creates\
the key/value pair in the map.
assert 'rocks' == m.get('expression')
assert [name: 'mrhaki', language: 'Groovy', expression: 'rocks'] == m

Original blog post written on November 3, 2009

16

Maps

Map with Default Values


In Groovy we can create a map and use the withDefault() method with a closure to define default values
for keys that are not yet in the map. The value for the key is then added to the map, so next time we can
get the value from the map.
def m = [start: 'one'].withDefault { key ->
key.isNumber() ? 42 : 'Groovy rocks!'
}
assert
assert
assert
assert

'one' == m.start
42 == m['1']
'Groovy rocks!' == m['I say']
3 == m.size()

// We can still assign our own values to keys of course:


m['mrhaki'] = 'Hubert Klein Ikkink'
assert 'Hubert Klein Ikkink' == m.mrhaki
assert 4 == m.size()

Original blog post written on July 14, 2010.

Determine Min and Max Entries in a Map


Since Groovy 1.7.6 we can use the min() and max() methods on a Map. We use a closure to define the
condition for a minimum or maximum value. If we use two parameters in the closure we must do a
classic comparison. We return a negative value if the first parameter is less than the second, zero if they
are equal, or a positive value if the first parameter is greater than the second parameter. If we use a single
parameter we can return a value that is used as Comparable for determining the maximum or minimum
entry in the Map.
def money = [cents: 5, dime: 2, quarter: 3]
// Determine max entry.
assert money.max { it.value }.value == 5
// Use String comparison for key.
assert money.max { it.key }.key == 'quarter'
// Use Comparator and compare key size.
assert money.max { a, b ->
a.key.size() <=> b.key.size()
}.key == 'quarter'
// Determine min entry.
assert money.min { it.value }.value == 2

Maps

// Use String comparison for key.


assert money.min { it.key }.key == 'cents'
// Use Comparator and compare key size.
assert money.min { a, b ->
a.key.size() <=> b.key.size()
}.key == 'dime'

Original blog post written on December 16, 2010.

17

You might also like