Can you break from a Groovy "each" closure?

Groovy

Groovy Problem Overview


Is it possible to break from a Groovy .each{Closure}, or should I be using a classic loop instead?

Groovy Solutions


Solution 1 - Groovy

Nope, you can't abort an "each" without throwing an exception. You likely want a classic loop if you want the break to abort under a particular condition.

Alternatively, you could use a "find" closure instead of an each and return true when you would have done a break.

This example will abort before processing the whole list:

def a = [1, 2, 3, 4, 5, 6, 7]

a.find { 
    if (it > 5) return true // break
    println it  // do the stuff that you wanted to before break
    return false // keep looping
}

Prints

1
2
3
4
5

but doesn't print 6 or 7.

It's also really easy to write your own iterator methods with custom break behavior that accept closures:

List.metaClass.eachUntilGreaterThanFive = { closure ->
    for ( value in delegate ) {
        if ( value  > 5 ) break
        closure(value)
    }
}

def a = [1, 2, 3, 4, 5, 6, 7]

a.eachUntilGreaterThanFive {
    println it
}

Also prints:

1
2
3
4
5    

Solution 2 - Groovy

Replace each loop with any closure.

def list = [1, 2, 3, 4, 5]
list.any { element ->
    if (element == 2)
        return // continue

    println element

    if (element == 3)
        return true // break
}

Output

1
3

Solution 3 - Groovy

No, you can't break from a closure in Groovy without throwing an exception. Also, you shouldn't use exceptions for control flow.

If you find yourself wanting to break out of a closure you should probably first think about why you want to do this and not how to do it. The first thing to consider could be the substitution of the closure in question with one of Groovy's (conceptual) higher order functions. The following example:

for ( i in 1..10) { if (i < 5) println i; else return}

becomes

(1..10).each{if (it < 5) println it}

becomes

(1..10).findAll{it < 5}.each{println it} 

which also helps clarity. It states the intent of your code much better.

The potential drawback in the shown examples is that iteration only stops early in the first example. If you have performance considerations you might want to stop it right then and there.

However, for most use cases that involve iterations you can usually resort to one of Groovy's find, grep, collect, inject, etc. methods. They usually take some "configuration" and then "know" how to do the iteration for you, so that you can actually avoid imperative looping wherever possible.

Solution 4 - Groovy

Just using special Closure

// declare and implement:
def eachWithBreak = { list, Closure c ->
  boolean bBreak = false
  list.each() { it ->
     if (bBreak) return
     bBreak = c(it)
  }
}

def list = [1,2,3,4,5,6]
eachWithBreak list, { it ->
  if (it > 3) return true // break 'eachWithBreak'
  println it
  return false // next it
}

Solution 5 - Groovy

You can't break from a Groovy each loop, but you can break from a java "enhanced" for loop.

def a = [1, 2, 3, 4, 5, 6, 7]

for (def i : a) {
    if (i < 2)
        continue
    if (i > 5)
        break
    println i
}

Output:

2
3
4
5

This might not fit for absolutely every situation but it's helped for me :)

Solution 6 - Groovy

I agree with other answers not to use an exception to break an each. I also do not prefer to create an extra closure eachWithBreak, instead of this I prefer a modern approach: let's use the each to iterate over the collection, as requested, but refine the collection to contain only those elements to be iterated, for example with findAll:

collection.findAll { !endCondition }.each { doSomething() }

For example, if we what to break when the counter == 3 we can write this code (already suggested):

(0..5)
    .findAll { it < 3  }
    .each { println it }

This will output

0
1
2

So far so good, but you will notice a small discrepancy though. Our end condition, negation of counter == 3 is not quite correct because !(counter==3) is not equivalent with it < 3. This is necessary to make the code work since findAll does not actually break the loop but continues until the end.

To emulate a real situation, let's say we have this code:

for (n in 0..5) {
    if (n == 3)
        break
    println n
}

but we want to use each, so let's rewrite it using a function to simulate a break condition:

def breakWhen(nr) { nr == 3 }
(0..5)
    .findAll { !breakWhen(it) }
    .each { println it }

with the output:

0
1
2
4
5

now you see the problem with findAll. This does not stop, but ignores that element where the condition is not met.

To solve this issues, we need an extra variable to remember when the breaking condition become true. After this moment, findAll must ignore all remaining elements.

This is how it should look like:

def breakWhen(nr) { nr == 3 }
def loop = true
(0..5)
    .findAll {
        if (breakWhen(it))
            loop = false
        !breakWhen(it) && loop
    } .each {
        println it
    }

with the output:

0
1
2

That's what we want!

Solution 7 - Groovy

(1..10).each{

if (it < 5)

println it

else

return false

Solution 8 - Groovy

You could break by RETURN. For example

  def a = [1, 2, 3, 4, 5, 6, 7]
  def ret = 0
  a.each {def n ->
    if (n > 5) {
      ret = n
      return ret
    }
  }

It works for me!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontinnyView Question on Stackoverflow
Solution 1 - GroovyTed NaleidView Answer on Stackoverflow
Solution 2 - GroovyMichal Z m u d aView Answer on Stackoverflow
Solution 3 - GroovyKai SternadView Answer on Stackoverflow
Solution 4 - Groovysea-kgView Answer on Stackoverflow
Solution 5 - GroovyrandomsimonView Answer on Stackoverflow
Solution 6 - GroovyCatalinView Answer on Stackoverflow
Solution 7 - GroovySagar Mal ShankhalaView Answer on Stackoverflow
Solution 8 - GroovyTseveen DView Answer on Stackoverflow