1. 概要

このチュートリアルでは、Kotlinでのコレクション集計操作について説明します。

2. Kotlinでの集計操作

Kotlinの集計操作は、配列やリストなどの要素のコレクションに対して実行され、コレクションの内容に基づいて単一の累積値を返します。

2.1. count()、sum()、および average()

count()関数を使用して、コレクション内の要素の数を見つけることができます。

val numbers = listOf(1, 15, 3, 8)
val count = numbers.count()

assertEquals(4, count)

続いて、コレクション内のすべての要素の合計を見つけるために、 sum()関数を使用できます。

val sum = numbers.sum()

assertEquals(27, sum)

同様に、コレクション内の要素の平均値を見つけるには、 average()関数を使用できます。

val average = numbers.average()

assertEquals(6.75, average)

2.2. sumBy()および sumByDouble()

sumBy()関数を使用して、コレクションの各要素にセレクター関数を適用することにより、マップされているすべての値の合計を見つけることができます。 この関数は常にinteger値を返します。 それにもかかわらず、 Byte、Short、Long、、およびFloat要素のリストでこの関数を使用することもできます。

val sumBy = numbers.sumBy { it * 5 }

assertEquals(135, sumBy)

double 値を返す関数を操作するには、 sumByDouble()関数を使用できます。

val sumByDouble = numbers.sumByDouble { it.toDouble() / 8 }

assertEquals(3.375, sumByDouble())

Kotlin 1.4の時点で、より優れたAPIで同じことを実現するために、オーバーロードされたバージョンが異なる新しい sumOf()メソッドがあります。

data class Employee(val name: String, val salary: Int, val age: UInt)

val employees = listOf(Employee("A", 3500, 23u), Employee("B", 2000, 30u))

assertEquals(5500, employees.sumOf { it.salary })
assertEquals(53u, employees.sumOf { it.age })

上記のように、同じ関数を使用してIntUIntの値を合計し、それぞれIntUIntの合計を返すことができます。 したがって、sumBy()やsumByDouble()のようにデータ型ごとに異なる名前の関数を使用する必要はありません。

具体的には、 sumOf()extension関数をInt Long Double UInt[で使用できます。 X131X]、 ULong BigInteger 、およびBigDecimalデータ型。

2.3. min()および max()

コレクション内の最大の要素を見つけるには、max()関数を使用できます。

val maximum = numbers.max()

assertEquals(15, maximum)

同様に、min()関数を使用して、コレクション内の最小の要素を見つけることができます

val minimum = numbers.min()

assertEquals(1, minimum)

コレクションに要素がない場合、両方の関数はnullを返します。

Kotlin 1.4以降、これらの関数は非推奨になりました。 maxOrNull()および minOrNull()という名前の2つの新しい関数があり、これらは他のコレクションと一致しています。 APIであり、何が返されるかについてもより明確になります。

これらの2つに加えて、次のセクションで説明するmaxおよびmin関連の関数も廃止され、新しいまたはNullの代替機能が採用されました。

2.4. maxBy()および minBy()

maxBy()および minBy()関数は、コレクション内の要素を比較可能なタイプに変換し、計算値で比較します。

指定されたセレクター関数から最大値を生成する最初の要素を見つけるには、 maxBy()関数を使用できます。

val maxBy = numbers.maxBy { it % 5 }

assertEquals(3, maxBy)

さらに、 minBy()関数を使用して、指定されたセレクター関数から最小値を生成する最初の要素を見つけることができます。

val minBy = numbers.minBy { it % 5 }

assertEquals(15, minBy)

コレクションに要素がない場合、これらの関数は両方ともnullを返します。

2.5. maxWith()および minWith()

maxWith()および minWith()関数は、コレクション内の要素を相互に比較し、コンパレータの戻り値で並べ替えます。

maxWith()を使用して、コンパレータオブジェクトに従って最大値を持つ最初の要素を見つけることができます。

val strings = listOf("Berlin", "Kolkata", "Prague", "Barcelona")
val maxWith = strings.maxWith(compareBy { it.length % 4 })

assertEquals("Kolkata", maxWith)

同様に、コンパレータオブジェクトに従って最小値を持つ最初の要素を返すには、 minWith()関数を使用できます。

val minWith = strings.minWith(compareBy { it.length % 4 })

assertEquals("Barcelona", minWith)

コレクションに要素がない場合、これらの関数は両方ともnullを返します。

3. 折りたたみと縮小機能

fold()および reduce()関数を使用して、要素のコレクションに操作を順番に適用し、累積結果を返すことができます。 これらの関数に必要な2つの引数は、累積値とコレクションの要素です。

2つの機能の違いは単純です fold()関数は初期値を取り、ラムダの最初の呼び出しはその初期値とコレクションの最初の要素をパラメーターとして受け取ります。 対照的に、 reduce()関数は初期値を取りません。代わりに、ラムダの最初の呼び出し中に、コレクションの1番目と2番目の要素をパラメーターとして使用します。

操作のデフォルト値を定義する必要がある場合は、 fold()を使用します。 または、 reduce()は、コレクションで定義されている値のみに操作が依存する場合に役立ちます。

もう1つの違いは、reduce()関数が空のコレクションで実行されると例外をスローすることです。 ただし、 fold()関数には初期値が必要なため、コレクションが空の場合はデフォルト値として使用され、例外はスローされません。

3.1. fold()および foldRight()

fold()関数は、アキュムレータの初期値を取ります。 次に、左から右への操作を現在のアキュムレータ値とコレクションの各要素に適用することにより、値を累積します。

val numbers = listOf(1, 15, 3, 8)
val result = numbers.fold(100) { total, it -> total - it }

assertEquals(73, result)

ラムダ関数の最初の呼び出しは、パラメーター100と1を使用します。

foldRight()関数はfold()関数と同様に機能しますが、コレクション内の要素を右から左にトラバースします。 さらに、操作引数の順序も、要素が最初に使用され、累積値が2番目に使用される場所で変更されます。

val result = numbers.foldRight(100) { it, total -> total - it }

assertEquals(73, result)

上記の例では、ラムダ関数の最初の呼び出しはパラメーター100と8を使用します。

3.2. foldIndexed()および foldRightIndexed()

foldIndexed()関数は、要素インデックスに基づいて操作を適用する場合に役立ちます。 初期値から値を累積し、左から右に操作を現在のアキュムレータ値と、定義されたコレクション内のインデックスを持つすべての要素に適用します。 さらに、foldIndexed()関数は、コレクションの累積値および要素とともに、操作引数として要素インデックスを取ります

val result = numbers.foldIndexed(100) { index, total, it ->
    if (index.minus(2) >= 0) total - it else total
}

assertEquals(89, result)

ここでのラムダ式の最初の呼び出しは、パラメーター100と1を使用します。 パラメーター1はラムダ式のインデックス条件を満たさないため、インデックス条件を満たしているため、次善のパラメーター3が呼び出されます。

foldRightIndexed()関数は、コレクションを右から左にトラバースしている間、 foldIndexed()関数と同様に機能します。

val result = numbers.foldRightIndexed(100) { index, it, total ->
    if (index.minus(2) >= 0) total - it else total
}

assertEquals(89, result)

この場合、ラムダ式の最初の呼び出しはパラメーター100と8を使用します。 パラメータ8はラムダ式のインデックス条件を満たすため、累積値に使用されます。

3.3. reduce()および reduceRight()

reduce()関数は、要素のリストがあり、それを単一の値に減らしたい場合に役立ちます。 この関数は、最初の要素から値を累積し、左から右への操作を現在のアキュムレータ値とコレクションの各要素に適用します。

val result = numbers.reduce { total, it -> total - it }

assertEquals(-25, result)

ここでのラムダへの最初の呼び出しは、パラメーター1と15を使用します。

reduceRight()関数はreduce()関数と同様に機能しますが、コレクションを右から左にトラバースします。 したがって、この関数は最後の要素から始まる値を累積します。

val result = numbers.reduceRight() { it, total -> total - it }

assertEquals(-11, result)

この場合、ラムダへの最初の呼び出しはパラメーター8と3を使用します。

3.4. reduceIndexed()および reduceRightIndexed()

reduceIndexed()関数は、最初の要素から値を累積し、左から右に操作を現在のアキュムレータ値と、定義されたコレクション内のインデックスを持つすべての要素に適用します。

val result = numbers.reduceIndexed { index, total, it ->
    if (index.minus(2) >= 0) total - it else total
}

assertEquals(-10, result)

ここでのラムダ式の最初の呼び出しは、パラメーター1と15を使用します。 パラメータ15はラムダ式のインデックス条件を満たさないため、次のパラメータ3がインデックス条件を満たしているために呼び出されます。

reduceRightIndexed()関数はreduceIndexed()関数と同様に機能しますが、コレクションを右から左にトラバースします

val result = numbers.reduceRightIndexed { index, it, total ->
    if (index.minus(2) >= 0) total - it else total
}

assertEquals(5, result)

この場合、ラムダ式の最初の呼び出しはパラメーター8と3を使用します。 パラメータ3はラムダ式のインデックス条件を満たすため、累積値に使用されます。

3.5. 中間結果

これまで、一連の値に次々に変換を適用する折りたたみ関数と縮小関数を見てきました。 このチェーンが終了すると、最終的に折りたたまれた値または縮小された値が返されます。

Kotlin 1.4以降、最終値に加えて、すべての中間結果にアクセスすることもできます。 具体的には、折りたたみには runningFold()を使用できます。

val numbers = listOf(1, 2, 3, 4, 5)
assertEquals(listOf(0, 1, 3, 6, 10, 15), numbers.runningFold(0) {total, it -> total + it})

上に示したように、この関数は、指定されたリストを初期値としてゼロで折り畳みます。 操作中は、すべての中間値を保持して返します。

runningReduce()を使用した削減についても同じことが言えます。

assertEquals(listOf(1, 3, 6, 10, 15), numbers.runningReduce { total, it -> total + it })

4. 結論

この記事では、コレクションを操作するためのKotlinでのさまざまな集計操作について説明しました。 Kotlinの機能の詳細については、Kotlinチュートリアルを参照してください。

いつものように、これらの例のコードはGitHubから入手できます。