Annidato forEach in Kotlin

1. Introduzione

In questo breve tutorial di Kotlin, esamineremo l'ambito dei parametri all'interno del lambda di un ciclo forEach .

Innanzitutto, definiamo i dati che useremo nei nostri esempi. In secondo luogo, vedremo come utilizzare forEach per iterare su un elenco. Terzo, vedremo come usarlo nei cicli annidati.

2. Dati di prova

I dati che utilizzeremo sono un elenco di paesi, ciascuno contenente un elenco di città, che a loro volta contengono un elenco di strade:

class Country(val name : String, val cities : List) class City(val name : String, val streets : List) 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. Semplice forEach

Per stampare il nome di ogni paese in lista, possiamo scrivere il seguente codice:

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

La sintassi precedente è simile a Java. Tuttavia, in Kotlin, se lambda accetta solo un parametro, si può utilizzare essa come il nome del parametro predefinito e non hanno bisogno di chiamare in modo esplicito:

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

Quanto sopra è anche equivalente a:

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

Vale la pena di notare che siamo in grado di utilizzare solo esso come un nome di parametro implicito se non c'è parametro esplicito.

Ad esempio, quanto segue non funziona:

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

E vedremo un errore in fase di compilazione:

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

4. annidato forEach

Se vogliamo iterare su tutti i paesi, le città e le strade, possiamo scrivere un ciclo annidato:

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

Qui, il primo si riferisce a un paese, il secondo è una città e il terzo è a una strada.

Tuttavia, se utilizziamo IntelliJ, viene visualizzato un avviso:

Implicit parameter 'it' of enclosing lambda is shadowed

Questo potrebbe non essere un problema, ma nella riga 6 non possiamo più fare riferimento al paese o alla città. Se lo vogliamo, dobbiamo nominare esplicitamente il parametro :

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

5. Alternative ai cicli annidati

I cicli annidati sono generalmente difficili da leggere e dovrebbero essere evitati se possibile. Un'opzione è usare flatMap () :

fun allStreetsFlatMap() { countries.flatMap { it.cities} .flatMap { it.streets} .forEach { println(it) } }

Tuttavia, se non usiamo una flatMap annidata , non possiamo accedere al nome della città o della via nell'istruzione println . Se vogliamo avere lo stesso output del metodo precedente allTable () ed evitare l'annidamento, potremmo aggiungere due funzioni di estensione:

fun City.getStreetsWithCityName() : List { return streets.map { "$name, $it" } .toList() } fun Country.getCitiesWithCountryName() : List { return cities.flatMap { it.getStreetsWithCityName() } .map { "$name, $it" } }

E poi usa questi due metodi con una singola flatMap :

fun allFlatMapTable() { countries.flatMap { it.getCitiesWithCountryName() } .forEach { println(it) } }

6. Conclusione

In questo breve articolo, abbiamo visto come utilizzare il parametro predefinito it in Kotlin e come accedere ai parametri di un forEach esterno dall'interno di un ciclo forEach annidato . Infine, abbiamo anche esaminato come evitare i loop annidati utilizzando flatMap e funzioni di estensione.

Tutti i frammenti di codice in questo articolo sono disponibili nel nostro repository GitHub.