Generic Top

Finally announcing my next course. The intro price of the upcoming “Learn Spring” course will permanently increase by $50 on Friday:

>> HAVE A LOOK

1. Introduction

In this short Kotlin tutorial, we’ll look at the parameter scope inside a forEach loop’s lambda.

First, we define the data which we’ll use in our examples. Second, we’ll see how to use forEach to iterate over a list. Third, we’ll look at how to use it in nested loops.

2. Test Data

The data we’ll use is a list of countries, each containing a list of cities, which in turn, contain a list of streets:

class Country(val name : String, val cities : List<City>)

class City(val name : String, val streets : List<String>)

class World {

    val streetsOfAmsterdam = listOf("Herengracht", "Prinsengracht")
    val streetsOfBerlin = listOf("Unter den Linden","Tiergarten")
    val streetsOfMaastricht = listOf("Grote Gracht", "Vrijthof")
    val countries = listOf(
      Country("Netherlands", listOf(City("Maastricht", streetsOfMaastricht),
        City("Amsterdam", streetsOfAmsterdam))),
      Country("Germany", listOf(City("Berlin", streetsOfBerlin))))
}

3. Simple forEach

To print the name of each country in the list, we can write the following code:

fun allCountriesExplicit() { 
    countries.forEach { c -> println(c.name) } 
}

The above syntax is similar to Java. However, in Kotlin, if the lambda accepts only one parameter, we can use it as the default parameter name and do not need to name it explicitly:

fun allCountriesIt() { 
    countries.forEach { println(it.name) } 
}

The above is also equivalent to:

fun allCountriesItExplicit() {
    countries.forEach { it -> println(it.name) }
}

It’s worthwhile to note that we can only use it as an implicit parameter name if there’s no explicit parameter.

For example, the following doesn’t work:

fun allCountriesExplicit() { 
    countries.forEach { c -> println(it.name) } 
}

And we’ll see an error at compile-time:

Error:(2, 38) Kotlin: Unresolved reference: it

4. Nested forEach

If we want to iterate over all countries, cities, and streets, we can write a nested loop:

fun allNested() {
    countries.forEach {
        println(it.name)
        it.cities.forEach {
            println(" ${it.name}")
            it.streets.forEach { println("  $it") }
        }
    }
}

Here, the first it refers to a country, the second it to a city and the third it to a street.

However, if we use IntelliJ, we see a warning:

Implicit parameter 'it' of enclosing lambda is shadowed

This might not be a problem, but, in line 6 we cannot refer to the country or city anymore. If we want that, we need to explicitly name the parameter:

fun allTable() {
    countries.forEach { c ->
        c.cities.forEach { p ->
            p.streets.forEach { println("${c.name} ${p.name} $it") }
        }
    }
}

5. Conclusion

In this short article, we saw how to use the default parameter it in Kotlin and how to access the parameters of an outer forEach from within a nested forEach loop.

All the code snippets in this article can be found in our GitHub repository.

Generic bottom

Finally announcing my next course. The intro price of the upcoming “Learn Spring” course will permanently increase by $50 on Friday:

>> HAVE A LOOK

5
Leave a Reply

avatar
2 Comment threads
3 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
4 Comment authors
MichaelOleksii FedorovLoredana CrusoveanuWil Carmon Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Oleksii Fedorov
Guest

It would be quite fantastic also to see what happens when you want to “break” or “continue” in the nested “forEach.” In these cases, one needs to provide a label for each lambda expression block, and use this label together with “[email protected]” Here is an example: world.cities.forEach [email protected] { city -> city.streets.forEach [email protected] { street -> if (street.name.endsWith("5")) { [email protected] } if (street.name.endsWith("3")) { [email protected] } // do something smart } }

Loredana Crusoveanu
Editor

Good point, thanks for adding the info here.
We’ve covered the return, break, continue statements in a separate post: https://www.baeldung.com/kotlin-return-break-continue

Cheers.

Oleksii Fedorov
Guest

Great! I haven’t seen this other post. Thank you.

Wil Carmon
Guest
Wil Carmon

You are re-creating “callback hell”

Why not use the flatMap operator or just explicit for-each loops?

Michael
Guest
Michael

Hi Wil,

I agree with you that forEach loops should not be nested too deeply and that in most cases a flatMap is the better solution. However, the article’s purpose was to show how nested forEach loops work. We could add a paragraph to suggest the use of flatMap as preferable alternative.