コトリンのネストforEach

1. 前書き

*この短いKotlinチュートリアルでは、__ forEach __loopのラムダ内のパラメータースコープを確認します*
最初に、例で使用するデータを定義します。 次に、_forEach_を使用してリストを反復処理する方法を説明します。 第三に、ネストされたループでそれを使用する方法を見ていきます。

2. テストデータ

使用するデータは国のリストであり、それぞれの国には都市のリストが含まれ、その都市のリストには通りのリストが含まれています。
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. シンプルな_forEach_

リスト内の各国の名前を印刷するには、次のコードを記述できます。
fun allCountriesExplicit() {
    countries.forEach { c -> println(c.name) }
}
上記の構文はJavaに似ています。 ただし、Kotlinでは、ラムダがパラメーターを1つだけ受け入れる場合、_it_をデフォルトのパラメーター名として使用でき、明示的に名前を付ける必要はありません。
fun allCountriesIt() {
    countries.forEach { println(it.name) }
}
上記も以下と同等です:
fun allCountriesItExplicit() {
    countries.forEach { it -> println(it.name) }
}
*明示的なパラメーターがない場合、暗黙のパラメーター名としてのみ_it_を使用できることに注意してください。*
たとえば、次は機能しません。
fun allCountriesExplicit() {
    countries.forEach { c -> println(it.name) }
}
そして、コンパイル時にエラーが表示されます。
Error:(2, 38) Kotlin: Unresolved reference: it

4. ネストされた_forEach_

すべての国、都市、および通りを反復処理する場合、ネストされたループを作成できます。
fun allNested() {
    countries.forEach {
        println(it.name)
        it.cities.forEach {
            println(" ${it.name}")
            it.streets.forEach { println("  $it") }
        }
    }
}
ここで、最初の_it_は国、2番目の_it_は都市、3番目の_it_は通りを指します。
ただし、IntelliJを使用すると、警告が表示されます。
Implicit parameter 'it' of enclosing lambda is shadowed
これは問題ではないかもしれませんが、6行目では、国または都市を参照することはできません。 *必要な場合は、パラメータに明示的に名前を付ける必要があります*:
fun allTable() {
    countries.forEach { c ->
        c.cities.forEach { p ->
            p.streets.forEach { println("${c.name} ${p.name} $it") }
        }
    }
}

5. 入れ子ループの代替

入れ子になったループは一般に読みにくく、可能であれば回避する必要があります。 1つのオプションは、_flatMap()_を使用することです。
fun allStreetsFlatMap() {
    countries.flatMap { it.cities}
      .flatMap { it.streets}
      .forEach { println(it) }
}
ただし、ネストされた_flatMap_を使用しない場合、_println_ステートメントで都市名または通り名にアクセスできません。 上記のメソッド_allTable()_と同じ出力を取得し、ネストを避けたい場合は、2つの拡張関数を追加できます。
fun City.getStreetsWithCityName() : List<String> {
    return streets.map { "$name, $it" }
      .toList()
}

fun Country.getCitiesWithCountryName() : List<String> {
    return cities.flatMap { it.getStreetsWithCityName() }
      .map { "$name, $it" }
}
そして、単一の_flatMap_でこれらの2つのメソッドを使用します。
fun allFlatMapTable() {
    countries.flatMap { it.getCitiesWithCountryName() }
      .forEach { println(it) }
}

6. 結論

この短い記事では、Kotlinでデフォルトパラメータ_it_を使用する方法と、ネストされた_forEach_ループ内から外側の_forEach_のパラメータにアクセスする方法を説明しました。 最後に、_flatMap_および拡張関数を使用してネストされたループを回避する方法も検討しました。
この記事のすべてのコードスニペットは、https://github.com/eugenp/tutorials/tree/master/core-kotlin [GitHubリポジトリ]にあります。