286

Kotlin has very nice iterating functions, like forEach or repeat, but I am not able to make the break and continue operators work with them (both local and non-local):

repeat(5) {
    break
}

(1..5).forEach {
    continue@forEach
}

The goal is to mimic usual loops with the functional syntax as close as it might be. It was definitely possible in some older versions of Kotlin, but I struggle to reproduce the syntax.

The problem might be a bug with labels (M12), but I think that the first example should work anyway.

It seems to me that I've read somewhere about a special trick/annotation, but I could not find any reference on the subject. Might look like the following:

public inline fun repeat(times: Int, @loop body: (Int) -> Unit) {
    for (index in 0..times - 1) {
        body(index)
    }
}
voddan
  • 31,956
  • 8
  • 77
  • 87
  • 1
    In current Kotlin you can indeed mimic this (while waiting for the `continue@label` and `break@label` features), see related question: http://stackoverflow.com/questions/34642868/how-do-i-do-a-break-or-continue-when-in-a-functional-loop-within-kotlin – Jayson Minard Jan 06 '16 at 21:14
  • 1
    This question could use clarification about whether you are asking only about the existance of `break` and `continue` for functional loops, or if you are seeking alternative answers that do exactly the same thing. The former appears to be the case, because you rejected the latter. – Jayson Minard Jan 06 '16 at 21:17
  • it seems they are added that in kotlin 1.3 – Tigran Babajanyan Nov 29 '18 at 07:52
  • @TigranBabajanyan wow! Do you have a link? – voddan Nov 30 '18 at 07:48
  • @voddan, no, I just tried it works – Tigran Babajanyan Nov 30 '18 at 08:08

12 Answers12

296

This will print 1 to 5. The return@forEach acts like the keyword continue in Java, which means in this case, it still executes every loop but skips to the next iteration if the value is greater than 5.

fun main(args: Array<String>) {
    val nums = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    nums.forEach {
       if (it > 5) return@forEach
       println(it)
    }
}

This will print 1 to 10 but skips 5.

fun main(args: Array<String>) {
    val nums = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    nums.forEach {
       if (it == 5) return@forEach
       println(it)
    }
}

This will print 1 to 4, and break when reaching 5.

fun main(args: Array<String>) {
    val nums = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    
    run breaking@ {
        nums.forEach {
           if (it == 5) return@breaking
           println(it)
        }
    }
}

Link to code snippet from ashuges.

Marcono1234
  • 5,856
  • 1
  • 25
  • 43
s-hunter
  • 24,172
  • 16
  • 88
  • 130
  • 8
    Great, but this still doesn't address the problem of not being able to end the forEach prematurely when some condition is met. It still keeps on executing the loop. – The Fox Oct 20 '19 at 13:30
  • 1
    @TheFox yes, it executes every loop and anything after the return is skipped when condition is met. Each operation in the forEach is a lambda function, currently there is no exact break operation for the forEach operation. The break is available in for loops, see: https://kotlinlang.org/docs/reference/returns.html – s-hunter Oct 28 '19 at 11:32
  • 4
    Here's a runnable Kotlin Playground snippet with both a `continue` and `break` example: https://pl.kotl.in/_LAvET-wX – ashughes Jun 30 '20 at 23:03
  • They reinvented goto" – gstackoverflow Jul 21 '23 at 12:01
212

Edit:
According to Kotlin's documentation, it is possible to simulate continue using annotations.

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach lit@ {
        if (it == 3) return@lit // local return to the caller of the lambda, i.e. the forEach loop
        print(it)
    }
    print(" done with explicit label")
}

If you want to simulate a break, just add a run block

fun foo() {
    run lit@ {
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@lit // local return to the caller of the lambda, i.e. the forEach loop
            print(it)
        }
        print(" done with explicit label")
    }
}

Original Answer:
Since you supply a (Int) -> Unit, you can't break from it, since the compiler do not know that it is used in a loop.

You have few options:

Use a regular for loop:

for (index in 0 until times) {
    // your code here
}

If the loop is the last code in the method
you can use return to get out of the method (or return value if it is not unit method).

Use a method
Create a custom repeat method method that returns Boolean for continuing.

public inline fun repeatUntil(times: Int, body: (Int) -> Boolean) {
    for (index in 0 until times) {
        if (!body(index)) break
    }
}
Kobato
  • 726
  • 1
  • 7
  • 15
Yoav Sternberg
  • 6,421
  • 3
  • 28
  • 30
  • Actually, my question was about making the specific syntax work, not about iterating. Don't you remember that it was possible at some Kotlin milestone? – voddan Sep 13 '15 at 10:11
  • 1
    I don't remember. But maybe it is because I don't use break & continue a lot. See [this issue](https://youtrack.jetbrains.com/issue/KT-1436), it says "Estimation - No estimation". – Yoav Sternberg Sep 13 '15 at 11:43
  • 2
    `break` and `continue` only work in loops. `forEach`, `repeat` and all the other methods are just that: methods and not loops. Yoav presented some alternatives but `break` and `continue` are just not ment to work for methods. – Kirill Rakhman Sep 13 '15 at 12:44
  • @YoavSternberg Brilliant! This peace of old docs is what I was looking for! So the feature is not implemented yet, left for future versions. If you care to create a separate answer, I'll mark it – voddan Sep 14 '15 at 08:16
  • In current Kotlin you can indeed mimic this (while waiting for the `continue@label` and `break@label` features), see related question: http://stackoverflow.com/questions/34642868/how-do-i-do-a-break-or-continue-when-in-a-functional-loop-within-kotlin – Jayson Minard Jan 06 '16 at 21:14
  • Instead of `for (index in 0..times - 1)` you can write: `for (index in 0 until times)`. – CoolMind May 27 '19 at 11:04
  • These are labels, not annotations. – Tenfour04 Nov 09 '21 at 19:15
  • `return@lit` returning to `run lit@` does not work if `forEach` is used on array created using `"abcd".chars()` i.e something like `"abcd".chars().forEach {...return@lit...}` – AdityaKapreShrewsburyBoston Jan 28 '22 at 01:49
  • Oh yeah, this is SOOOO much simpler and more intuitive than java. I'm so glad we switched. – SMBiggs Mar 23 '22 at 19:25
82

A break can be achieved using:

//Will produce "12 done with nested loop"
//Using "run" and a tag will prevent the loop from running again.
//Using return@forEach if I>=3 may look simpler, but it will keep running the loop and checking if i>=3 for values >=3 which is a waste of time.
fun foo() {
    run loop@{
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@loop // non-local return from the lambda passed to run
            print(it)
        }
    }
    print(" done with nested loop")
}

And a continue can be achieved with:

//Will produce: "1245 done with implicit label"
fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) return@forEach // local return to the caller of the lambda, i.e. the forEach loop
        print(it)
    }
    print(" done with implicit label")
}

As anyone here recommends... read the docs :P https://kotlinlang.org/docs/reference/returns.html#return-at-labels

EDIT: While the main question asks about forEach, it's important to consider the the good old "for". Using Kotlin doesn't mean we need to use forEach all the time. Using the good old "for" is perfectly ok, and sometimes even more expressive and concise than forEach:

fun foo() {
    for(x in listOf(1, 2, 3, 4, 5){
        if (x == 3) break //or continue
        print(x)
    }
    print("done with the good old for")
}
Robert
  • 6,855
  • 4
  • 35
  • 43
Raymond Arteaga
  • 4,355
  • 19
  • 35
  • Nice solution. Works very well. Though it seems like without using `@loop` gives the same desired result as well. – Paras Sidhu Jun 17 '20 at 05:30
  • In fact, you can omit the explicit tag "@loop" and use the implicit one "@run". The key aspect here is the local return to the caller of the lambda. Note you need to wrap the loop inside some scope so you can local return on it later on. – Raymond Arteaga Jun 17 '20 at 23:26
  • This really answers the question, however i think this probably isn't the right path to take for functional programming. What we need is, from Lodash, `transform` where you can break under certain condition, ex. `reduceReturnIf(acc, value, returnIf: func)` – windmaomao Aug 15 '20 at 17:54
41

As the Kotlin documentation says, using return is the way to go. Good thing about Kotlin is that if you have nested functions, you can use labels to explicitly write where your return is from:

Function Scope Return
fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        /** Non-local return directly to the caller of foo(). */
        if (it == 3) return
        print(it)
    }

    println("this point is unreachable")
}
Local Return

It doesn't stop going through forEach loop (it's like a continue in for loop).

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach lit@{
        /** Local return to the caller of the lambda, i.e. the forEach loop. */
        if (it == 3) return@lit
        print(it)
    }

    print(" done with explicit label")
}

Check out the documentation, it's really good :)

SerjantArbuz
  • 982
  • 1
  • 12
  • 16
cesards
  • 15,882
  • 11
  • 70
  • 65
  • 4
    Warning: return@lit does not stop `forEach` – Jemshit Dec 11 '18 at 14:46
  • That is correct. It's intended. The first solution it does, but if you have instructions inside a loop you can choose where do you want to return to / jump to. In the second case, if we just use return it will stop ;-) – cesards Dec 12 '18 at 15:44
  • 3
    Calling Return@lit likes Continue – pqtuan86 Feb 21 '20 at 02:55
31

You can use return from lambda expression which mimics a continue or break depending on your usage.

This is covered in the related question: How do I do a "break" or "continue" when in a functional loop within Kotlin?

Community
  • 1
  • 1
Jayson Minard
  • 84,842
  • 38
  • 184
  • 227
19

continue type behaviour in forEach

list.forEach { item -> // here forEach give you data item and you can use it 
    if () {
        // your code
        return@forEach // Same as continue
    }

    // your code
}

for break type behaviour you have to use for in until or for in as per the list is Nullable or Non-Nullable

  1. For Nullable list:

    for (index in 0 until list.size) {
        val item = list[index] // you can use data item now
        if () {
            // your code
            break
        }
    
        // your code
    }
    
  2. For Non-Nullable list:

    for (item in list) { // data item will available right away
        if () {
            // your code
            break
        }
    
        // your code
    }
    
Sumit Jain
  • 1,100
  • 1
  • 14
  • 27
8

I have the perfect solution for this (:

list.apply{ forEach{ item ->
    if (willContinue(item)) return@forEach
    if (willBreak(item)) return@apply
}}
ar36el
  • 946
  • 1
  • 9
  • 14
2

Break statement for nested loops forEach():

listOf("a", "b", "c").forEach find@{ i ->
    listOf("b", "d").forEach { j ->
        if (i == j) return@find
        println("i = $i, j = $j")
    }
}

Result:

i = a, j = b
i = a, j = d
i = c, j = b
i = c, j = d

Continue statement with anonymous function:

listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
    if (value == 3) return
    print("$value ")
})

Result:

1 2 4 5 
alexrnov
  • 2,346
  • 3
  • 18
  • 34
2

Maybe change forEach to this:

for (it in myList) {
    if (condition) {
        doSomething()
    } else {
        break // or continue
    }
} 

It works for HashMap's:

 for (it in myMap) {
     val k = it.key
     val v = it.value

     if (condition) {
         doSomething()
     } else {
         break // or continue
     }
 }
SerjantArbuz
  • 982
  • 1
  • 12
  • 16
Hesham Fas
  • 876
  • 1
  • 9
  • 20
1

It seems much better use For loop like this: for (item in list){ break }

Konstantin Konopko
  • 5,229
  • 4
  • 36
  • 62
0
fun part2(ops: List<Int>): Int = ops.asSequence()
    .scan(0) { acc, v -> acc + v }
    .indexOf(-1)

If you can afford to turn a collection into a sequence, normally the cost is trivial, then you should be able to take advantage of the deferred feature.

You might already notice asSequence in the above. It's here for saving us going over the entire list. Right after we have a match via indexOf, it'll stop. Bingo! Saving us write a while here.

as in Part 2 of medium article.

SerjantArbuz
  • 982
  • 1
  • 12
  • 16
windmaomao
  • 7,120
  • 2
  • 32
  • 36
0

If the condition depends on the outcome of a previous element in the list, you can use sequence and takeWhile to execute depth-first lazily.

sequenceOf(1, 2, 3, 4, 5).map { i ->
    println("i = ${i}")
    i < 3
}.takeWhile { success ->
    println("success = ${success}")
    success
}.toList()

will print

i = 1
success = true
i = 2
success = true
i = 3
success = false

You need the terminal toList() in the end to execute the sequence.

More details: https://kotlinlang.org/docs/sequences.html#sequence

Tomas
  • 514
  • 1
  • 5
  • 15