Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you break from a Groovy "each" closure?

Tags:

groovy

People also ask

Why is groovy looping?

Looping is an essential feature in every Programming Language including Groovy. Loops a control flow statement for traversing or iterating items in a collection, array, list, set, etc. There's a lot of ways to loop through collection, array, list, or set in 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    

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

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.


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
}

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 :)