コレクション変換操作
Kotlin標準ライブラリは、コレクションの_変換_のための拡張関数セットを提供します。 これらの関数は、提供された変換ルールに基づいて既存のコレクションから新しいコレクションを構築します。 このページでは、利用可能なコレクション変換関数の概要を説明します。
Map
_マッピング_変換は、別のコレクションの要素に対する関数の結果からコレクションを作成します。 基本的なマッピング関数は、map()
です。 これは、指定されたラムダ関数を各要素に順次適用し、ラムダの結果のリストを返します。 結果の順序は、元の要素の順序と同じです。 さらに要素のインデックスを引数として使用する変換を適用するには、mapIndexed()
を使用します。
fun main() {
val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 })
println(numbers.mapIndexed { idx, value -> value * idx })
}
変換が特定の要素でnull
を生成する場合、map()
の代わりにmapNotNull()
関数を呼び出すか、mapIndexed()
の代わりにmapIndexedNotNull()
関数を呼び出すことで、結果コレクションからnull
を除外できます。
fun main() {
val numbers = setOf(1, 2, 3)
println(numbers.mapNotNull { if ( it == 2) null else it * 3 })
println(numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx })
}
マップを変換する場合、キーを変換して値をそのままにする、またはその逆を行うという2つのオプションがあります。 キーに指定された変換を適用するには、mapKeys()
を使用します。一方、mapValues()
は値を変換します。 どちらの関数も、マップエントリを引数として受け取る変換を使用するため、キーと値の両方を操作できます。
fun main() {
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
println(numbersMap.mapKeys { it.key.uppercase() })
println(numbersMap.mapValues { it.value + it.key.length })
}
Zip
_ジップ_変換とは、2つのコレクションの同じ位置にある要素からペアを構築することです。 Kotlin標準ライブラリでは、これはzip()
拡張関数によって行われます。
コレクションまたは配列で別のコレクション(または配列)を引数としてzip()
を呼び出すと、Pair
オブジェクトのList
が返されます。レシーバーコレクションの要素は、これらのペアの最初の要素となります。
コレクションのサイズが異なる場合、zip()
の結果は小さい方のサイズとなり、大きい方のコレクションの最後の要素は結果に含まれません。
zip()
は、infix形式のa zip b
でも呼び出すことができます。
fun main() {
val colors = listOf("red", "brown", "grey")
val animals = listOf("fox", "bear", "wolf")
println(colors zip animals)
val twoAnimals = listOf("fox", "bear")
println(colors.zip(twoAnimals))
}
また、レシーバー要素と引数要素の2つのパラメータを取る変換関数とともにzip()
を呼び出すこともできます。 この場合、結果のList
には、レシーバーと引数要素の同じ位置のペアに対して呼び出された変換関数の戻り値が含まれます。
fun main() {
val colors = listOf("red", "brown", "grey")
val animals = listOf("fox", "bear", "wolf")
println(colors.zip(animals) { color, animal -> "The ${animal.replaceFirstChar { it.uppercase() }} is $color"})
}
Pair
のList
がある場合、これらのペアから2つのリストを構築する逆の変換、つまり_unzip_(展開)を行うことができます。
- 最初のリストには、元のリスト内の各
Pair
の最初の要素が含まれます。 - 2番目のリストには、2番目の要素が含まれます。
ペアのリストを展開するには、unzip()
を呼び出します。
fun main() {
val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4)
println(numberPairs.unzip())
}
Associate
_関連付け_変換では、コレクション要素とそれに関連付けられた特定の値からマップを構築できます。 異なる関連付けの種類では、要素が関連付けマップのキーまたは値のいずれかになります。
基本的な関連付け関数associateWith()
は、元のコレクションの要素がキーとなり、指定された変換関数によって値が生成されるMap
を作成します。2つの要素が等しい場合、マップには最後の要素のみが残ります。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers.associateWith { it.length })
}
コレクション要素を値とするマップを構築するには、associateBy()
関数があります。 これは、要素の値に基づいてキーを返す関数を受け取ります。2つの要素のキーが等しい場合、マップには最後の要素のみが残ります。
associateBy()
は、値変換関数とともに呼び出すこともできます。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers.associateBy { it.first().uppercaseChar() })
println(numbers.associateBy(keySelector = { it.first().uppercaseChar() }, valueTransform = { it.length }))
}
キーと値の両方がコレクション要素から何らかの形で生成されるマップを構築するもう1つの方法は、associate()
関数です。 これは、Pair
(対応するマップエントリのキーと値)を返すラムダ関数を受け取ります。
注意点として、associate()
は短命なPair
オブジェクトを生成するため、パフォーマンスに影響を与える可能性があります。 したがって、associate()
はパフォーマンスが重要でない場合や、他のオプションよりも好ましい場合にのみ使用すべきです。
後者の例は、キーと対応する値が要素から同時に生成される場合です。
fun main() {
data class FullName (val firstName: String, val lastName: String)
fun parseFullName(fullName: String): FullName {
val nameParts = fullName.split(" ")
if (nameParts.size == 2) {
return FullName(nameParts[0], nameParts[1])
} else throw Exception("Wrong name format")
}
val names = listOf("Alice Adams", "Brian Brown", "Clara Campbell")
println(names.associate { name -> parseFullName(name).let { it.lastName to it.firstName } })
}
ここでは、最初に要素に対して変換関数を呼び出し、その関数の結果のプロパティからペアを構築しています。
Flatten
ネストされたコレクションを操作する場合、ネストされたコレクションの要素へのフラットなアクセスを提供する標準ライブラリ関数が役立つでしょう。
最初の関数は、flatten()
です。 これは、コレクションのコレクション、例えばSet
のList
に対して呼び出すことができます。 この関数は、ネストされたコレクションのすべての要素を単一のList
として返します。
fun main() {
val numberSets = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(1, 2))
println(numberSets.flatten())
}
もう1つの関数、flatMap()
は、ネストされたコレクションを処理するための柔軟な方法を提供します。これは、コレクション要素を別のコレクションにマップする関数を受け取ります。 結果として、flatMap()
はすべての要素に対するその戻り値の単一リストを返します。 つまり、flatMap()
は、map()
(マッピング結果としてのコレクションを含む)とflatten()
を連続して呼び出したかのように動作します。
data class StringContainer(val values: List<String>)
fun main() {
val containers = listOf(
StringContainer(listOf("one", "two", "three")),
StringContainer(listOf("four", "five", "six")),
StringContainer(listOf("seven", "eight"))
)
println(containers.flatMap { it.values })
}
文字列表現
コレクションの内容を読みやすい形式で取得する必要がある場合は、コレクションを文字列に変換する関数、joinToString()
およびjoinTo()
を使用します。
joinToString()
は、指定された引数に基づいてコレクション要素から単一のString
を構築します。 joinTo()
も同様の処理を行いますが、結果を指定されたAppendable
オブジェクトに追記します。
デフォルトの引数で呼び出すと、これらの関数はコレクションでtoString()
を呼び出すのと同様の結果を返します。つまり、要素の文字列表現がカンマとスペースで区切られたString
です。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers)
println(numbers.joinToString())
val listString = StringBuffer("The list of numbers: ")
numbers.joinTo(listString)
println(listString)
}
カスタムの文字列表現を構築するには、関数引数separator
、prefix
、およびpostfix
でそのパラメータを指定できます。結果の文字列はprefix
で始まり、postfix
で終わります。separator
は最後の要素を除く各要素の後に配置されます。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers.joinToString(separator = " | ", prefix = "start: ", postfix = ": end"))
}
より大きなコレクションの場合、結果に含める要素の数であるlimit
を指定したい場合があります。 コレクションのサイズがlimit
を超える場合、他のすべての要素はtruncated
引数の単一の値に置き換えられます。
fun main() {
val numbers = (1..100).toList()
println(numbers.joinToString(limit = 10, truncated = "<...>"))
}
最後に、要素自体の表現をカスタマイズするには、transform
関数を提供します。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers.joinToString { "Element: ${it.uppercase()}"})
}