Groovy `each` - or why everything is a nail
With the aspiring groovyist there is a tendency to use each
for
everything. When coming from an imperative programming language you did all
the heavy lifting with for
, while
, if
, etc. so each
gets
adopted again for that style of programming. This at some point will end with
the question: how to break out of an each. And the simple answer is: you
can't (well, you shouldn').
In Groovy many things look like some basic instruction from the language, so
each
looks like some loop instruction from the Groovy language, right?
[1,2,3].each{ println it }
? Well actually each
is just a method, that
takes a clojure as param, for this list and Groovy allows you leave out
parentheses in that case. Does this still look like something basic like
for
: [1,2,3].each({ println it })
?
What does each
do then? It runs a for
loop and passes each element to
your provided closure. Your return value is ignored. No break
, no
continue
makes sense here. Actually return
early in your closure
provides continue
behaviour. Also note, that the return value of the
each
method is the list itself.
So here are a few each
anti-patterns:
each
instead of a map:
// NO! def results = [] [1,2,3].each{ results << (it*it) } // YES! def results = [1,3,4].collect{ it*it }
each
instead of a filter:
// NO! def results = [] [1,2,3].each{ if (it>1) { results << it } } // YES! def results = [1,2,3].findAll{ it>1 }
each
to find first:
// NO, really NO! def result [1,2,3].each{ if (it==2) { result = it break // does not work } } // YES! def result = [1,2,3].find{ it==2 }
each
instead of a reduce:
// NO! def sum = 0 [1,2,3].each{ sum += it } // YES def sum = [1,2,3].sum() // or as an actual reduce, if you prefer: def sum = [1,2,3].inject(0) { r,it -> r += it }
So what when you really need the behaviour of for
? Well it's not
forbidden in groovy. Just use it, it's even faster than each
.