コレクション変換操作
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
_ジッピング_変換は、両方のコレクションの同じ位置にある要素からペアを構築することです。Kotlin標準ライブラリでは、これはzip()
拡張関数によって行われます。
コレクションまたは配列に対して、別のコレクション(または配列)を引数として呼び出すと、zip()
はPair
オブジェクトのList
を返します。レシーバーコレクションの要素は、これらのペアの最初の要素になります。
コレクションのサイズが異なる場合、zip()
の結果はより小さい方のサイズになります。より大きいコレクションの最後の要素は結果に含まれません。
zip()
は中置記法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))
}
zip()
を、レシーバー要素と引数要素という2つのパラメータを受け取る変換関数と共に呼び出すこともできます。この場合、結果の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つのリストを構築する逆変換(アンジッピング)を行うことができます。
- 最初のリストには、元のリストの各
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 })
}
String representation
コレクションの内容を読みやすい形式で取得する必要がある場合は、コレクションを文字列に変換する関数である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()}"})
}