Grails Cookbook - A collection of tutorials and examples

Groovy Range Examples

Groovy supports a quick way of declaring a list of sequential values using Ranges. In Groovy, a Range can be defined using the .. or ..< notation to declare a list of sequential values. Below are some examples.

Groovy IntRage

The most common Range in Groovy is the IntRange. We can quickly create a sequential list of integers using the .. or ..< notation. Here is a simple example:
def myIntRage = 6..10
println "Class is: ${myIntRage.class}"
println "Implements List?: ${myIntRage instanceof java.util.List}"
println "Contents are: ${myIntRage}"
println "First item: ${myIntRage[0]}"
println "Second item: ${myIntRage[1]}"
println "Third item: ${myIntRage[2]}"
println "Fourth item: ${myIntRage[3]}"
println "Fifth item: ${myIntRage[4]}"
The notation .. creates a list of numbers from 6 up to 10. It is a quick way of creating a sequential list without actually declaring each particular content, which could be tedious or unpractical. Here is the expected output:
Class is: class groovy.lang.IntRange
Implements List?: true
Contents are: [6, 7, 8, 9, 10]
First item: 6
Second item: 7
Third item: 8
Fourth item: 9
Fifth item: 10
Because it is a list, it supports many functions inherent to a List.
def myIntRage = 6..10
println "Size is: ${myIntRage.size()}"
println "Item at index 2 is: ${myIntRage.get(2)}"
println "Does it contain 8?: ${myIntRage.contains(8)}"
println "Does it contain 11?: ${myIntRage.contains(11)}"
println "Does it contain 7 and 10?: ${myIntRage.containsAll([7, 10])}"
println "Does it contain 9 and 12?: ${myIntRage.containsAll([9, 12])}"
println "What is the index of the item with vaue 7?: ${myIntRage.indexOf(7)}"
println "Is the list empty?: ${myIntRage.isEmpty()}"

And the result is shown below. Note that the methods invoked are part of Java's java.util.List interface.

Size is: 5
Item at index 2 is: 8
Does it contain 8?: true
Does it contain 11?: false
Does it contain 7 and 10?: true
Does it contain 9 and 12?: false
What is the index of the item with vaue 7?: 1
Is the list empty?: false

We can iterate an IntRage using for loop, example:

def myIntRange = 11..15
for (n in myIntRange) {
	println n
}

Will display:

11
12
13
14
15

And also iterate using Groovy each:

def myIntRange = 21..25
myIntRange.each { n ->
	println n
}

Will output:

21
22
23
24
25

We can define a range in decreasing value:

def myIntRage = 100..90
println "Values: ${myIntRage}"

Will yield the output:

Values: [100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90]

Increments of IntRange don't necessarily need to be 1. It can be customized using step. Example:

def myIntRage = (1..10).step(2)
println "Values: ${myIntRage}"
def anotherIntRage = (2..10).step(2)
println "Values: ${anotherIntRage}"
def yetAnotherIntRage = (2..10).step(3)
println "Values: ${yetAnotherIntRage}"

Will have the output below. Notice that the first element is always the from value. Succeeding values follows the step defined.

Values: [1, 3, 5, 7, 9]
Values: [2, 4, 6, 8, 10]
Values: [2, 5, 8]

And we can also do step in reverse order. For example:

def myIntRage = (10..1).step(2)
println "Values: ${myIntRage}"
def anotherIntRage = (9..1).step(2)
println "Values: ${anotherIntRage}"
def yetAnotherIntRage = (10..1).step(3)
println "Values: ${yetAnotherIntRage}"

Will output the following:

Values: [10, 8, 6, 4, 2]
Values: [9, 7, 5, 3, 1]
Values: [10, 7, 4, 1]

Half Open IntRage

We can define an IntRange where the to bound is exclusive, meaning it is not included in the values. Here is an example:
def myIntRange = 11..<15
println "Values: ${myIntRange}"

The list will have the values from 11 to 15, but excluding the upper bound 15.

Values: [11, 12, 13, 14]

We can declare half open on a decreasing range of values:

def myIntRange = 25..<21
println "Values: ${myIntRange}"

Will yield the result:

Values: [25, 24, 23, 22]

Groovy ObjectRange

We can also use Range on non Integer values, this will have the instance of ObjectRange. Here is an example:

def myObjectRange = 'a'..'c'
println "Class: ${myObjectRange.class}"
println "Values: ${myObjectRange}"

This is the output. It creates an ObjectRange with three items, the Strings 'a', 'b', and 'c':

Class: class groovy.lang.ObjectRange
Values: [a, b, c]

We can do more than iterating over characters. For example:

def myObjectRange1 = 'Ball1'..'Ball5'
def myObjectRange2 = 'Balla'..'Balle'
println "Values: ${myObjectRange1}"
println "Values: ${myObjectRange2}"

Will have the output below.

Values: [Ball1, Ball2, Ball3, Ball4, Ball5]
Values: [Balla, Ballb, Ballc, Balld, Balle]

It is smart enough when only the last character will increment. But not smart enough when two characters are involved. For example:

def myObjectRange = 'Ball11'..'Ball22'

Will throw an exception:

Caught: java.lang.IllegalArgumentException: Incompatible Strings for Range: String#next() will not reach the expected value
java.lang.IllegalArgumentException: Incompatible Strings for Range: String#next() will not reach the expected value
	at Test.run(Test.groovy:1)

But is smart enough to understand reverse range:

def myObjectRange = 'Ball5'..'Ball1'
println "Values: ${myObjectRange}"

Will yield:

Values: [Ball5, Ball4, Ball3, Ball2, Ball1]

ObjectRange also allows non integer values. For example:

def myObjectRange = 1.5..5
println "Class: ${myObjectRange.class}"
println "Values: ${myObjectRange}"

The behavior is similar to IntRange where the step defaults to 1.

Class: class groovy.lang.ObjectRange
Values: [1.5, 2.5, 3.5, 4.5]

But we can use step explicitly:

def myObjectRange = (1.5..10).step(2) 
println "Values: ${myObjectRange}"

Will print:

Values: [1.5, 3.5, 5.5, 7.5, 9.5]

But we may not use non integer step. For example:

def myObjectRange = (1.5..10).step(1.5)

Will throw:

Caught: groovy.lang.MissingMethodException: No signature of method: groovy.lang.ObjectRange.step() is applicable for argument types: (java.math.BigDecimal) values: [1.5]
Possible solutions: step(int), step(int, groovy.lang.Closure), grep(), grep(), sleep(long), grep(java.lang.Object)
groovy.lang.MissingMethodException: No signature of method: groovy.lang.ObjectRange.step() is applicable for argument types: (java.math.BigDecimal) values: [1.5]
Possible solutions: step(int), step(int, groovy.lang.Closure), grep(), grep(), sleep(long), grep(java.lang.Object)
	at Test.run(Test.groovy:1)