`break` and `continue` in `forEach` in Kotlin

LoopsForeachLambdaKotlin

Loops Problem Overview


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

Loops Solutions


Solution 1 - Loops

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.

Solution 2 - Loops

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
    }
}

Solution 3 - Loops

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")
}

Solution 4 - Loops

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

This is covered in the related question: https://stackoverflow.com/questions/34642868/how-do-i-do-a-break-or-continue-when-in-a-functional-loop-within-kotlin

Solution 5 - Loops

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 explicity write where your return is from:

Function Scope Return

fun foo() {
  listOf(1, 2, 3, 4, 5).forEach {
    if (it == 3) return // non-local return directly to the caller of foo()
    print(it)
  }
  println("this point is unreachable")
}

and Local Return (it doesn't stop going through forEach = continuation)

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")
}

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

Solution 6 - Loops

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
    }
    

Solution 7 - Loops

I have the perfect solution for this (:

list.apply{ forEach{ item ->
    if (willContinue(item)) return@forEach
    if (willBreak(item)) return@apply
}}

Solution 8 - Loops

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 

Solution 9 - Loops

maybe change forEach to

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

it works for hashmaps

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

       if(condition){
         doSomething()
       }else{
         break //or continue
        }
    }


Solution 10 - Loops

  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 https://medium.com/@windmaomao/kotlin-day-1-up-and-down-38885a5fc2b1

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
QuestionvoddanView Question on Stackoverflow
Solution 1 - Loopss-hunterView Answer on Stackoverflow
Solution 2 - LoopsYoav SternbergView Answer on Stackoverflow
Solution 3 - LoopsRaymond ArteagaView Answer on Stackoverflow
Solution 4 - LoopsJayson MinardView Answer on Stackoverflow
Solution 5 - LoopscesardsView Answer on Stackoverflow
Solution 6 - LoopsSumit JainView Answer on Stackoverflow
Solution 7 - Loopsar36elView Answer on Stackoverflow
Solution 8 - LoopsalexrnovView Answer on Stackoverflow
Solution 9 - LoopsHesham FasView Answer on Stackoverflow
Solution 10 - LoopswindmaomaoView Answer on Stackoverflow