Groovy Makes Iteration Easy

Out of the box, groovy gives you a number of powerful methods to iterate over lists and maps:

def fibList = [1, 1, 2, 3, 5]

fibList.each { println it }  // prints all of the numbers in the list
assert fibList.any { it == 3 }
assert fibList.every { it > 0 }
assert fibList.collect { it - 1 } == [0, 0, 1, 2, 4]
assert fibList.findAll { it > 1 && it < 5 } == [2, 3]
assert fibList.find { it > 1 } == 2
assert fibList.inject("fib: ") { str, val -> str << val }.toString() == "fib: 11235"

That's really nice if you're working with raw lists and maps, but what if you have a class that doesn't extend list or map? How hard is it to empower that class with the groovy iteration methods? If this were Java, you'd likely need to implement an interface with these methods (and throw a "not implemented" exception for those you didn't feel like taking the time to implement).

Since it's not Java, but groovy (and you've read the title of this blog post :), you know it's easy!

All you have to do is put a public method on your class called iterator() that returns an Iterator.

If you have a member variable that has an iterator you can expose, you can just use that:

class EvenFoo {
   private numbers = [2, 4, 6, 8, 10]
   Iterator iterator() {
       return numbers.iterator()
   }
}

def evens = new EvenFoo()
assert evens.each { println it } // prints even numbers 2 through 10
assert evens.findAll { it > 5 && it < 9 } == [6, 8]
assert evens.any { it == 4 }
assert evens.every { it > -1 }
assert evens.collect { it } == [2, 4, 6, 8, 10]
assert evens.inject("evens: ") { str, val -> str << val }.toString() == "evens: 246810"

You can also use the power of groovy to implement a subset of the Iterator interface using a map (just define hasNext and next). Return that, and it's just as good as a regular Iterator from groovy's perspective (duck typing rocks!):

class EvenFoo {
   def i = 0
   def max = 10
   Iterator iterator() {
       i = 0
       return [ hasNext: { i < max }, next: { i += 2 } ] as Iterator
   }
}

def evens = new EvenFoo()
assert evens.each { println it } // prints even numbers 2 through 10
assert evens.findAll { it > 5 && it < 9 } == [6, 8]
assert evens.any { it == 4 }
assert evens.every { it > -1 }
assert (evens.collect { it }) == [2, 4, 6, 8, 10]
assert evens.inject("evens: ") { str, val -> str << val }.toString() == "evens: 246810"

This is a contrived example, but I've run into a number of situations where I want to iterate over something that isn't a list or map. It can be especially useful in lazy loading situations (such as a file/directory scanner or a SAX parser).

8 Responses to “Groovy Makes Iteration Easy”

  1. Matthew Taylor

    Ted, great Groovy stuff! You gotta love a language that continues to surprise and amaze with all its features. I never realized it was as easy as providing an iterator to give a class iteration features. Thanks for the tip!

  2. Hamlet D'Arcy

    It might be worth pointing out that all those iteration methods are defined on Object, not List or Map. All objects in Groovy respond to any(), each(), and every(). See: http://groovy.codehaus.org/groovy-jdk/java/lang/Object.html

    After learning this, my code started to look very, very loopy because you can define algorithms that work across data structures treating everything as a List… if an algorithm encounters a non-list then the each() method never iterates without throwing an exception. Nice stuff.

  3. Peter

    Heh. I spent several hours yesterday trying to do this. I was reading GinA and trying out all kinds of code until I came up with a solution that looks almost exactly like yours (except that I believed I had to make it a MethodClosure data.&iterator()). If I had only checked GroovyBlogs, I would have had the solution right away. :)

  4. tednaleid

    Thanks for the feedback guys, I’m glad you found it useful!

    @Hamlet: I figured this out in sort of a similar way myself. One day I was messing around with the metaclass and had it list all of the metaMethods on a custom class that I had.

    class Foo {}
    
    println new Foo().metaClass.metaMethods*.name.unique().sort()
    
    /* prints 
    
    ["addShutdownHook", "any", "asType", "collect", "dump", 
    "each", "eachWithIndex", "every", "find", "findAll", 
    "findIndexOf", "findIndexValues", "findLastIndexOf", "getAt",
    "getMetaClass", "getMetaPropertyValues", "getProperties",
    "grep", "identity", "inject", "inspect", "invokeMethod", "is", 
    "isCase", "iterator", "print", "printf", "println", "putAt", 
    "sleep", "sprintf", "use", "with"]
    */
    

    I saw that all of the iteration methods were already on there. After an initial “WTF?” and some digging into the groovy source, I figured out what was going on.

    I eventually found DefaultGroovyMethods which is a goldmine of groovy treasures. It’s also by far the largest class in the groovy source tree (~10,300 lines of code!). Almost all of the whizzy stuff that you get without doing an import originates in that file.

  5. Josh

    Thanks for posting this, Ted – it’s sometimes difficult to find documentation for iteration in Groovy. This is great!

  6. Jeff Winkler

    Actually, the two forms of for loops will work with your example. For loops execute about 5X faster than each() on small sets, and have debugging advantages since you’re not dealing with a closure.

Leave a Reply