Search

Dark theme | Light theme
Showing posts with label GroovyGoodness:Collections. Show all posts
Showing posts with label GroovyGoodness:Collections. Show all posts

September 10, 2025

Groovy Goodness: Using Index Operator For Streams

Groovy already has a lot of extra methods for Stream classes. With Groovy 5 the getAt method is added as a terminal operation. You can now use the [] syntax to get an item from a Stream from a specific position. The argument is the index of the item or a range with the start and end index of the items to get.

September 2, 2025

Groovy Goodness: For Loops With Index Variable

Groovy 5 adds support for using an index variable in a for-in loop. You must define an extra variable as first argument of the for loop. This variable will be used as index variable. For each iteration of the code in the for-in loop the value of this index variable is incremented by 1. You can use this value in the for loop.

August 30, 2025

Groovy Goodness: Use Range With subList Method

Groovy is known for exending standard Java classes with extra methods or extra arguments for existing methods. Since Groovy 5 you can use a range as argument for the List.subList method. The range is used to determine the begin and end index of the original List instance to return.

August 29, 2025

Groovy Goodness: Transform Iterable Into A Map

In a previous blog post you can see how to use the collectEntries method to transform an iterable object into a Map. Groovy 5 introduced some extra overloaded methods for collectEntries. You can now pass a closure or function as arguments to transform the original iterable element into the key and value for the resulting Map. It is now also possible to pass a so-called collector Map that will be used to extend with new key/value pairs.

Besides extra overloaded method signatures for collectEntries Groovy 5 also adds the new methods withCollectedKeys and withCollectedValues. With the method withCollectedKeys a closure or function can passed to create the keys for the new Map based on the elements from the iterable. The value of the key/value pair is the unchanged element. You use the method withCollectedValues to pass a closure or function to create the value for the new key/value pair in the resulting Map. The key will be the original element from the iterable.

April 21, 2023

Groovy Goodness: Sorting Data With GINQ

GINQ (Groovy-INtegerate Query) is part of Groovy since version 4. With GINQ we can use SQL-like queries to work with in-memory data collections. If we want to sort the data we can use orderby followed by the property of the data we want to sort just like in SQL we can use order by. By default the sort ordering is ascending and null values are put last. We can change the sort ordering by specifying in desc with the orderby clause. Or to make the ascending order explicitly we use the statement in asc. Each of asc and desc also can take an argument to specify how we want null values to be sorted. The default way is to keep null values last in the ordering. If we want to make this explicit we use nullslast as argument to asc or desc. To have null values in the sorted result first we use the argument nullsfirst.

The following example shows all use cases for using orderby when using GINQ:

import groovy.json.JsonSlurper

// Parse sample JSON with a list of users.
def json = new JsonSlurper().parseText('''[
{ "username": "mrhaki", "email": "mrhaki@localhost" },
{ "username": "mrhaki", "email": "user@localhost" },
{ "username": "hubert", "email": "user@localhost" },
{ "username": "hubert", "email": "hubert@localhost" },
{ "username": "hubert", "email": null }
]''')

// Helper method to return a String
// representation of the user row.
def formatUser(row) {
    row.username + "," + row.email
}

// Default ordering is ascending.
// We specify the field name we want to order on.
assert GQ {
    from user in json
    orderby user.username
    select formatUser(user)
}.toList() == [
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'hubert,null',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost'
]

// We can explicitly set ordering to ascending.
assert GQ {
    from user in json
    orderby user.email in asc
    select formatUser(user)
}.toList() == [
    'hubert,hubert@localhost',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost',
    'hubert,user@localhost',
    'hubert,null'
]

// By default null values are last.
// We can also make this explicit as
// option to in asc() or in desc().
assert GQ {
    from user in json
    orderby user.email in asc(nullslast)
    select formatUser(user)
}.toList() == [
    'hubert,hubert@localhost',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost',
    'hubert,user@localhost',
    'hubert,null'
]

// We can combine multiple properties to sort on.
assert GQ {
    from user in json
    orderby user.username, user.email
    select formatUser(user)
}.toList() == [
    'hubert,hubert@localhost',
    'hubert,user@localhost',
    'hubert,null',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost'
]

// To order descending we must specify it
// as in desc.
assert GQ {
    from user in json
    orderby user.username in desc
    select formatUser(user)
}.toList() == [
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost',
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'hubert,null'
]

// We can mix the ordering and set it
// differently for each property.
assert GQ {
    from user in json
    orderby user.username in asc, user.email in desc
    select formatUser(user)
}.toList() == [
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'hubert,null',
    'mrhaki,user@localhost',
    'mrhaki,mrhaki@localhost'
]

// By default all null values are last,
// but we can use nullsfirst to have null
// values as first value in the ordering.
assert GQ {
    from user in json
    orderby user.username in asc, user.email in desc(nullsfirst)
    select formatUser(user)
}.toList() == [
    'hubert,null',
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'mrhaki,user@localhost',
    'mrhaki,mrhaki@localhost'
]

Written with Groovy 4.0.11.

April 13, 2023

Groovy Goodness: Using Tuples

Groovy supports a tuple type. A tuple is an immutable object to store elements of potentially different types. In Groovy there is a separate Tuple class based on how many elements we want to store in the tuple. The range starts at Tuple0 and ends with Tuple16. So we can store a maximum of 16 elements in a Groovy tuple.
Each of the classes has a constructor that takes all elements we want to store. But the Tuple class also has static factory methods to create those classes. We can use the tuple method and based on how many elements we provide to this method we get the corresponding Tuple object.

To get the elements from a Tuple instance we can use specific properties for each element. The first element can be fetched using the v1 property, the second element is v2 and so on for each element. Alternatively we use the subscript operator ([]) where the first element is at index 0.

Each Tuple instance also has a subList and subTuple method where we can provide the from and to index values of the elements we want to be returned. The methods return a new Tuple with only the elements we requested.
A Groovy tuple is also a List and that means we can use all collection methods for a List also on a Tuple instance.

In the following example we create some tuples and use different methods:

// Using the constructor to create a Tuple.
def tuple2 = new Tuple2("Groovy", "Goodness")

// We can also use the static tuple method.
// Maximum number of elements is 16.
def tuple3 = Tuple.tuple("Groovy", "is", "great")

assert tuple3 instanceof Tuple3


// We can mix types as each elements can
// have it's own type.
def mixed = Tuple.tuple(30, "minutes")

// We can use the subscript operator ([])
// to get a value.
assert mixed[0] == 30
assert mixed[1] == "minutes"

// Or use the get() method.
assert mixed.get(0) instanceof Integer
assert mixed.get(1) instanceof String

// Or use the getter/property V1/V2.
// For each element in a Tuple we can use that.
// Notice that the first element starts with v1.
assert mixed.v1 == 30
assert mixed.getV2() == "minutes"

// Or use multiple assignments.
def (int minutes, String period) = mixed
assert minutes == 30
assert period == "minutes"


// We can get the size.
assert mixed.size() == 2


// Or transform the elements to an array
// and type information is saved.
assert mixed.toArray() == [30, "minutes"]

assert mixed.toArray()[0].class.name == "java.lang.Integer"
assert mixed.toArray()[1].class.name == "java.lang.String"


// Sample tuple with 4 elements.
Tuple4 tuple4 = Tuple.tuple("Groovy", "rocks", "as", "always")

// We can use subList or subTuple to create a new Tuple
// with elements from the original Tuple.
// We need to specify the "from" and "to" index.
// The "to" index is exclusive.
assert tuple4.subList(0, 2) == Tuple.tuple("Groovy", "rocks")
assert tuple4.subTuple(0, 2) == Tuple.tuple("Groovy", "rocks")

// As Tuple extends from List we can use all
// Groovy collection extensions.
assert tuple4.findAll { e -> e.startsWith("a") } == ["as", "always"]
assert tuple4.collect { e -> e.toUpperCase() } == ["GROOVY", "ROCKS", "AS", "ALWAYS"]


// We can even create an empty Tuple.
assert Tuple.tuple() instanceof Tuple0

Written with Groovy 4.0.11.

July 4, 2022

Groovy Goodness: Closed And Open Ranges

Groovy supports ranges for a long time. But Groovy 4 adds a new feature for ranges and that is the support for open (exclusive) ranges at the beginning of a range. Open means the number that defines the range is not part of the actual range result and we must use the less-than character (<). This is also referred to as exclusive, where the value is excluded from the range. When a range is closed the value is included, also called inclusive. Before Groovy 4 we could already define the end of the range to be exclusive or inclusive, but now we can also define the beginning of the range to be exclusive.

In the following example we use closed and open range definitions from the start or end:

def inclRange = 0..5

assert inclRange == [0, 1, 2, 3, 4, 5]
assert inclRange.from == 0
assert inclRange.to == 5


def exclEndRange = 0..<5

assert exclEndRange == [0, 1, 2, 3, 4]
assert exclEndRange.from == 0
assert exclEndRange.to == 4


// Support for exclusive begin added in Groovy 4.
def exclBeginRange = 0<..5

assert exclBeginRange == [1, 2, 3, 4, 5]
assert exclBeginRange.from == 1
assert exclBeginRange.to == 5


// Support for exclusive begin added in Groovy 4.
def exclRange = 0<..<5

assert exclRange == [1, 2, 3, 4]
assert exclRange.from == 1
assert exclRange.to == 4

Written with Groovy 4.0.3.

June 28, 2022

Groovy Goodness: Get Row Number In GINQ Result Set

GINQ (Groovy-INtegrated Query) is added since Groovy 4. With GINQ we can query in-memory collections with SQL like statements. If we want to get the row numbers for each row in the query result set we can use the implicit variable _rn. We must specify _rn in the select expression of our GINQ query. We can even use as to give it a meaningful name in the result set.

In the following example we have a basic GINQ query where use _rn to get the row number of each row in the query result set:

def letters = GQ {
    from letter in ['a', 'b', 'c']
    select _rn, letter
}.collect { item -> [item._rn, item.letter] }

assert letters == [[0, 'a'], [1, 'b'], [2, 'c']]

In the following example we first parse JSON and then use GINQ to query it. We see in the example that the row number is based on the result after the where expression is applied:

import groovy.json.JsonSlurper

def json = new JsonSlurper().parseText('''
[
  {
    "id": 1001,
    "language": "Groovy"
  },
  {
    "id": 1002,
    "language": "Clojure"
  }, 
  {
    "id": 1003,
    "language": "Java"
  }
]
''')

def languages = GQ {
  from l in json
  where l.language == "Groovy"
  // We add 1 to _rn to make it 1-based instead of 0-based. 
  // Also we use as rowNumber to give a meaningful name.
  select _rn + 1 as rowNumber, l.id as id, l.language as name
}.collect { item -> [row: item.rowNumber, name: item.name] }

// Also notice the row number is calculated based
// on the result after applying the where clause.
assert languages.first() == [row: 1, name: "Groovy"]

Written with Groovy 4.0.3.

March 3, 2020

Groovy Goodness: Safe Index Based Access For Lists, Arrays and Maps

Groovy 3 adds the feature of safe index based access for lists, arrays and maps. This means we can use ?[index] to get or a set a value on a list or array without getting a NullPointerException when the list or array is not initialised. With maps we can use ?[key] for getting a value or set a value and when the map object is not initialised we don't get a NullPointerException.

In the following example we see several examples of setting or getting values using indices or keys:

def list = null

// Accessing null list or array 
// using index based access, 
// will throw NullPointerException when
// collection is null.
try {
    list[0]
} catch (NullPointerException npe) {
    assert npe
}

// Using ?[...] will not throw NullPointerException.
assert list?[0] == null

// Assignment are ignored when list is null.
list?[1] = 42
assert list?[1] == null

// When collection is not null we simply
// get the default behaviour for index based
// access of elements.
def values = ["Groovy", "rocks"]
assert values?[0] == "Groovy"
values?[2] = '!'
assert values?.join(" ") == "Groovy rocks !"

// We can use ?[key] for maps as well.
def info = null
assert info?['address'] == null
info?['address'] = 'unknown'
assert info?['address'] == null

def user = [alias: 'mrhaki', loves: 'Groovy']
assert user?['alias'] == 'mrhaki'
user?['country'] = 'The Netherlands'
assert user?['country'] == 'The Netherlands'
assert user?['age'] == null

Written with Groovy 3.0.1.

February 21, 2020

Groovy Goodness: Check Item Is Not In A Collection With !in

Groovy contains lots of little gems that just make live easier and our code more expressive. Groovy 3 doesn't disappoint by adding some nice syntax additions. For example we can now use !in to check if an item is not in a collection, opposed to in that checks if an item is in a collection.

In the following example we use !in:

def list = ['Groovy', 3, 'is', 'awesome', '!']

// New Groovy 3 syntax to check item 
// is NOT in a collection.
assert 'Java' !in list

// Old syntax.
assert !('Java' in list)

// Just to show in still works.
assert 'Groovy' in list

Written with Groovy 3.0.1.

February 19, 2020

Groovy Goodness: Shuffle List or Array

In Java we can use Collections.shuffle method to randomly reorder items in a list. Groovy 3.0.0 adds the shuffle and shuffled methods to a List or array directly. The implementation delegates to Collections.shuffle. The shuffle method will reorder the original list, so there is a side effect using this method. Or we can use the shuffled method that will return a copy of the original list where the items are randomly ordered.

In the next example we use both methods to randomly order lists:

def signs = (2..10) + ['J', 'Q', 'K', 'A']
def symbols = ['♣', '♦', '♥', '♠']

// Create list as [♣2, ♦2, ♥2, ♠2, ..., ♣A, ♦A, ♥A, ♠A] 
def cards = [symbols, signs].combinations().collect { it.join() }

// Store original cards list.
def deck = cards.asImmutable()

// We should have 52 cards.
assert cards.size() == 52

// Let's shuffle the cards.
// Notice this will change the cards list.
cards.shuffle()
 
assert cards.every { card -> deck.contains(card) }

println cards.take(5) // Possible output: [♣6, ♠A,  ♥Q, ♦Q, ♠5]

// We can use our own Random object for shuffling.
cards.shuffle(new Random(42))

assert cards.every { card -> deck.contains(card) }

println cards.take(5)  // Possible output: [♦5, ♦2, ♦3, ♣7, ♦J]


// Store first 5 cards.
def hand = cards.take(5)

// Using shuffled we get a new list 
// with items in random order. 
// The original list is not changed.
def shuffledCards = cards.shuffled()

assert shuffledCards.size() == cards.size()
assert shuffledCards.every { card -> cards.contains(card) }

// Original list has not changed.
assert hand == cards.take(5)

println shuffledCards.take(5) // Possible output: [♣4, ♠2, ♠6, ♥Q, ♦4]

// We can pass our own Random object.
def randomizer = new Random(42)
def randomCards = cards.shuffled(randomizer)

assert randomCards.size() == cards.size()
assert randomCards.every { card -> cards.contains(card) }

println randomCards.take(5) // Possible output: [♥5, ♠6, ♠8, ♣3, ♠4]

Written with Groovy 3.0.0.

February 12, 2020

Groovy Goodness: Calculate Average For Collection

Groovy 3 adds the average method to collections to calculate the average of the items in the collections. When the items are numbers simply the average is calculated. But we can also use a closure as argument to transform an item into a number value and then the average on that number value is calculated.

In the following example code we use the average method on a list of numbers and strings. And we use a closure to first transform an element before calculating the average:

def numbers = [10, 20, 30, 40, 50]

assert numbers.average() == 30

// We can use a closure to transform an item
// and the result is used for calculating an average.
assert numbers.average { n -> n / 10 } == 3


def words = ['Groovy', 'three', 'is', 'awesome']

// Use supported Java method reference syntax to first 
// get length of word.
assert words.average(String::size) == 5

// Calculate average number of vowels in the words.
assert words.average { s -> s.findAll(/a|e|i|o|u/).size() } == 2.25

Written with Groovy 3.0.0.

January 12, 2020

Groovy Goodness: Transform Elements While Flattening

We can use the flatten method in Groovy to flatten a collection that contains other collections into a single collection with all elements. We can pass a closure as extra argument to the flatten method to transform each element that is flattened. The argument of the closure is the element from the original collection.

In the following example we first use the flatten method without a closure argument. Then we pass a closure argument and transform the element:

def list = [1, [2, 3], [[4]]]

// Simple flatten the nested collections.
assert list.flatten() == [1, 2, 3, 4]

// We can use a closure to transform
// the elements in the resulting collection.
assert list.flatten { it * 2 } == [2, 4, 6, 8]

Written with Groovy 2.5.7.

June 25, 2018

Groovy Goodness: Preorder And Postorder Tree Traversal

The Node class in Groovy has the methods depthFirst and breadthFirst to return a collection of Node objects using either depth or breadth first traversal. Since Groovy 2.5.0 we can specify if we want to use preorder (the default) or postorder traversal. Also the methods now accept a Closure that will be invoked for each visited node. The Closure has the current Node as first argument, the second argument is the tree level of the current node.

In the following example we read some XML and then use depthFirst in several ways to visit the tree of nodes:

// We start with a XML node hierarchy.
def xml = '''
        <A>
          <B>
            <D/>
            <E/>
          </B>
          <C>
            <F/>
          </C>
        </A>
        '''
def root = new XmlParser().parseText(xml)

// Preorder traversal is default, but
// we can also specify it with the boolean
// argument of depthFirst method.
assert root.depthFirst(true)
           .collect { node -> node.name() } == ['A', 'B', 'D', 'E', 'C', 'F']
           
// Groovy 2.5.0 adds possibility to
// directly call closure for 
// each node visited where the first
// Closure argument is the node and
// the second argument the level.
def result = []
root.depthFirst { node, level -> result << "$level${node.name()}" }

assert result == ['1A', '2B', '3D', '3E', '2C', '3F']

// Postorder traversal can be specified
// by setting preorder argment to false.
// When used in combination with Closure
// argument we must using named argument
// preorder.
result = []
root.depthFirst(preorder: false) { node -> result << node.name() }

assert result == ['D', 'E', 'B', 'F', 'C', 'A']

In our second example we use the breadthFirst method. This means the nodes for visited per level in the tree:

// Let's create a Node hierarchy.
def builder = NodeBuilder.newInstance()
def root = builder.A {
    B {
        D()
        E()
    }
    C {
        F()
    }
}


// Preorder traversal is default, but
// we can also specify it with the boolean
// argument of breadthFirst method.
assert root.breadthFirst(true)
           .collect { node -> node.name() } == ['A', 'B', 'C', 'D', 'E', 'F']
           
// Groovy 2.5.0 adds possibility to
// directly call closure for 
// each node visited with node and level.
def result = []
root.breadthFirst { node, level -> result << "$level${node.name()}" }

assert result == ['1A', '2B', '2C', '3D', '3E', '3F']

// Postorder traversal is implemented
// as starting at the lowest level and 
// working our way up.
result = []
root.breadthFirst(preorder: false) { node -> result << node.name() }

assert result == ['D', 'E', 'F', 'B', 'C', 'A']

Written with Groovy 2.5.0.

June 22, 2018

Groovy Goodness: Tuples With Up To 9 Items

A tuple is an ordered, immutable list of elements. Groovy supported tuples with one or two elements before Groovy 2.5.0. Since Groovy 2.5.0 we can use tuples with maximal nine items. Groovy added the classes Tuple3 up to Tuple9. The bonus we get compared to an unmodifiable list with elements is that we can use properties like first, second, third, fourth, fifth, sixth, seventh, eighth, ninth to get items at the specified position.

In the following example we use different Tuple classes:

// We can define the types of the elements when we 
// construct a tuple.
def tuple3 = new Tuple3<String, Integer, BigDecimal>('add', 2, 40.0)

// We can use first, second, third properties
// to get values from the tuple.
assert tuple3.first == 'add'
assert tuple3.second == 2
assert tuple3.third == 40.0

// We can use the [index] syntax to get element.
assert tuple3[0] == 'add'  

// Fully typed tuple.
Tuple4<String, Integer, BigDecimal, Integer> tuple4 = 
    new Tuple4<>('subtract', 100, 55.0, 3)
    
assert tuple4.first == 'subtract'
assert tuple4.second == 100
assert tuple4.third == 55.0
assert tuple4.fourth == 3
assert tuple4[-1] == 3

// With subTuple we can get subsequent
// values from the tuple as a new tuple.
assert tuple4.subTuple(2, tuple4.size()) == new Tuple2<BigDecimal, Integer>(55.0, 3)

// We can imagine how to work with Tuple4..Tuple8 :-)
// ...

// Finally a tuple with 9 items.
def tuple9 = new Tuple9('Groovy', 'rocks', 'and', 'is', 'fun', 'to', 'use', 'as', 'language')

assert tuple9.fifth == 'fun'
assert tuple9.sixth == 'to'
assert tuple9.seventh == 'use'
assert tuple9.eighth == 'as'
assert tuple9.ninth == 'language'

// Tuple extends AbstractList, so we can
// use all methods from List as well.
assert tuple9.join(' ') == 'Groovy rocks and is fun to use as language'

Written with Groovy 2.5.0.

June 21, 2018

Groovy Goodness: Unmodifiable Collections

When we wanted to create collections in Groovy that were unmodifiable we could use asImmutable. Since Groovy 2.5.0 we can also use the asUnmodifiable method on collections. The method can be applied on all Collection types including Map.

In the following example we use asUnmodifiable on a List and Map:

import static groovy.test.GroovyAssert.shouldFail

// Create List that is unmodifiable.
def list = ['Groovy', 'Gradle', 'Asciidoctor', 'Micronaut'].asUnmodifiable()

shouldFail(UnsupportedOperationException) {
    // We cannot add new items.
    list << 'Java'
}
    
shouldFail(UnsupportedOperationException) {
    // We cannot change items.
    list[0] = 'Java'
}


// Create Map that is unmodifiable.
def data = [name: 'Messages from mrhaki', subject: 'Gr8 stuff'].asUnmodifiable()

shouldFail(UnsupportedOperationException) {
    // We cannot add a new key.
    data.subject = 'Dev subjects'
}
    
shouldFail(UnsupportedOperationException) {
    // We cannot change the value of a key.
    data.blog = true
}

Written with Groovy 2.5.0.

June 13, 2018

Groovy Goodness: Remove Last Item From List Using RemoveLast Method (And Pop/Push Methods Reimplemented)

Versions of Groovy before 2.5.0 implemented pop and push methods for the List class for items at the end of a List object. The pop method removed the last item of a List and push added a item to the List. Groovy 2.5.0 reimplemented the methods so they now work on the first item of a List instance. To remove an item from the end of the list we can use the newly added method removeLast.

In the following example Groovy code we use the removeLast and add methods to remove and add items to the end of the list. And with the pop and push methods we remove and add items to the beginnen of the list:

def list = ['Groovy', 'is', 'great!']
 
// Remove last item from list
// with removeLast().
assert list.removeLast() == 'great!'
assert list == ['Groovy', 'is']
 
// Remove last item which is now 'is'.
list.removeLast()
 
// Add new item to end of the list.
list.add 'rocks!'
 
assert list.join(' ') == 'Groovy rocks!'


/* IMPORTANT */
/* pop() and push() implementations has changed */
/* in Groovy 2.5.0. They now work on the first */
/* item in a List instead of the last. */

// Using pop() we remove the first item
// of a List.
assert list.pop() == 'Groovy'

// And with push we add item to 
// beginning of a List.
list.push 'Spock'

assert list.join(' ') == 'Spock rocks!'

Written with Groovy 2.5.0.

Groovy Goodness: Getting All Init And Tail Values Recursively

For a long time we could get the tail or init values for a collection. Groovy 2.5.0 adds the methods inits and tails for Iterable objects. These methods return a List with List values where the first element is the original collection and the next is the result of init or tail on the previous element. This is repeated until the result of init or tail is an empty List.

In the next example script we have a original collection of letters. We first run the init and tail methods (without the s). Next we look at the result of invoking inits and tails:

def letters = ('a'..'d').toList()

assert letters == ['a', 'b', 'c', 'd']

assert letters.init() == ['a', 'b', 'c']
assert letters.tail() == ['b', 'c', 'd']

// Inits returns collection of all init()
// results for an Iterable. The first element
// has the original values, the next element
// the result of init()
// of the previous element and so on until
// an empty List is the result.
assert letters.inits() == [
    ['a', 'b', 'c', 'd'], 
    ['a', 'b', 'c'], 
    ['a', 'b'], 
    ['a'], 
    []]

// Tails returns collection of all tail()
// results for an Iterable. The first element
// has the original values, the next element
// the result of tail()
// of the previous element and so on until
// an empty List is the result.
assert letters.tails() == [
    ['a', 'b', 'c', 'd'], 
    ['b', 'c', 'd'], 
    ['c', 'd'], 
    ['d'], 
    []]

Written with Groovy 2.5.0.

June 12, 2018

Groovy Goodness: Intersect Collections With Custom Comparator

In a previous post we learned about the intersect method added to collections in Groovy. Since Groovy 2.5.0 we can supply a custom Comparator to the intersect method to define our own rules for the intersection.

In the following example we first apply the intersect method with the default Comparator. Then we create a new Comparator using a closure where we check if the value is in both collections and if the value starts with the letter M:

def stuff = ['Groovy', 'Gradle', 'Grails', 'Spock', 'Micronaut', 'Ratpack'] as Set
def micro = ['Ratpack', 'Micronaut', 'SpringBoot', 'Microservice']

// Using default comparator to get values
// that are in both collections.
assert stuff.intersect(micro) == ['Ratpack', 'Micronaut'] as Set
assert micro.intersect(stuff) == ['Micronaut', 'Ratpack']

// Comparator to check if value is in
// both collection and starts with a 'M'.
def microName = { a, b -> 
    def comp = a <=> b
    comp == 0 ? a[0] == 'M' ? 0 : -1 : comp
} as Comparator

// This time we use the Comparator and
// end up with all elements in both
// collections that start with a 'M'.
assert stuff.intersect(micro, microName) == ['Micronaut'] as Set
assert micro.intersect(stuff, microName) == ['Micronaut']

Written with Groovy 2.5.0.

June 11, 2018

Groovy Goodness: Java 8 Stream Enhancements

Groovy 2.5.0 adds several methods to make working with Java 8 Streams more Groovy. First of all the methods toList and toSet are added to the Stream class. These methods will convert the stream to a List and Set using the Stream.collect method with Collectors.toList and Collectors.toSet as argument. Furthermore we can convert any array object to a Stream using the stream method that is added to all array objects.

In the following example we use the support of converting an array to a Stream and then getting a List and Set from the stream:

def sample = ['Groovy', 'Gradle', 'Grails', 'Spock'] as String[]

def result = sample.stream()  // Use stream() on array objects
                   .filter { s -> s.startsWith('Gr') } 
                   .map { s -> s.toUpperCase() }
                   .toList()  // toList() added to Stream by Groovy
                   
assert result == ['GROOVY', 'GRADLE', 'GRAILS']


def numbers = [1, 2, 3, 1, 4, 2, 5, 6] as int[]

def even = numbers.stream()  // Use stream() on array objects
                  .filter { n -> n % 2 == 0 }
                  .toSet()  // toSet() added to Stream 
                  
assert even == [2, 4, 6] as Set

Written with Groovy 2.5.0.