Skip to content

コレクション変換操作

Kotlin標準ライブラリは、コレクションの_変換_のための拡張関数セットを提供します。 これらの関数は、提供された変換ルールに基づいて既存のコレクションから新しいコレクションを構築します。 このページでは、利用可能なコレクション変換関数の概要を説明します。

Map

_マッピング_変換は、別のコレクションの要素に対する関数の結果からコレクションを作成します。 基本的なマッピング関数は、map()です。 これは、指定されたラムダ関数を各要素に順次適用し、ラムダの結果のリストを返します。 結果の順序は、元の要素の順序と同じです。 さらに要素のインデックスを引数として使用する変換を適用するには、mapIndexed()を使用します。

kotlin

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を除外できます。

kotlin

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()は値を変換します。 どちらの関数も、マップエントリを引数として受け取る変換を使用するため、キーと値の両方を操作できます。

kotlin

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でも呼び出すことができます。

kotlin

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には、レシーバーと引数要素の同じ位置のペアに対して呼び出された変換関数の戻り値が含まれます。

kotlin

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"})
}

PairListがある場合、これらのペアから2つのリストを構築する逆の変換、つまり_unzip_(展開)を行うことができます。

  • 最初のリストには、元のリスト内の各Pairの最初の要素が含まれます。
  • 2番目のリストには、2番目の要素が含まれます。

ペアのリストを展開するには、unzip()を呼び出します。

kotlin

fun main() {
    val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4)
    println(numberPairs.unzip())
}

Associate

_関連付け_変換では、コレクション要素とそれに関連付けられた特定の値からマップを構築できます。 異なる関連付けの種類では、要素が関連付けマップのキーまたは値のいずれかになります。

基本的な関連付け関数associateWith()は、元のコレクションの要素がキーとなり、指定された変換関数によって値が生成されるMapを作成します。2つの要素が等しい場合、マップには最後の要素のみが残ります。

kotlin

fun main() {
    val numbers = listOf("one", "two", "three", "four")
    println(numbers.associateWith { it.length })
}

コレクション要素を値とするマップを構築するには、associateBy()関数があります。 これは、要素の値に基づいてキーを返す関数を受け取ります。2つの要素のキーが等しい場合、マップには最後の要素のみが残ります。

associateBy()は、値変換関数とともに呼び出すこともできます。

kotlin

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()はパフォーマンスが重要でない場合や、他のオプションよりも好ましい場合にのみ使用すべきです。

後者の例は、キーと対応する値が要素から同時に生成される場合です。

kotlin

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()です。 これは、コレクションのコレクション、例えばSetListに対して呼び出すことができます。 この関数は、ネストされたコレクションのすべての要素を単一のListとして返します。

kotlin

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()を連続して呼び出したかのように動作します。

kotlin

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です。

kotlin

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)
}

カスタムの文字列表現を構築するには、関数引数separatorprefix、およびpostfixでそのパラメータを指定できます。結果の文字列はprefixで始まり、postfixで終わります。separatorは最後の要素を除く各要素の後に配置されます。

kotlin

fun main() {
    val numbers = listOf("one", "two", "three", "four")    
    println(numbers.joinToString(separator = " | ", prefix = "start: ", postfix = ": end"))
}

より大きなコレクションの場合、結果に含める要素の数であるlimitを指定したい場合があります。 コレクションのサイズがlimitを超える場合、他のすべての要素はtruncated引数の単一の値に置き換えられます。

kotlin

fun main() {
    val numbers = (1..100).toList()
    println(numbers.joinToString(limit = 10, truncated = "<...>"))
}

最後に、要素自体の表現をカスタマイズするには、transform関数を提供します。

kotlin

fun main() {
    val numbers = listOf("one", "two", "three", "four")
    println(numbers.joinToString { "Element: ${it.uppercase()}"})
}