言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

Kotlin辞典

  1. トップページ
  2. Kotlin辞典
  3. リスト — zip() / unzip()

リスト — zip() / unzip()

対応: Kotlin 1.0(2016)

Kotlinの『zip()』は2つのリストを要素ごとにペアにしたリストを返します。『unzip()』はペアのリストを2つのリストに分解します。リストの並列処理や、複数のデータ列を結合する場合に使います。

構文

val names = listOf("member_a", "member_b", "member_c")
val scores = listOf(85, 72, 91)

// zip — 対応する要素を Pair にします
val zipped = names.zip(scores)
// [Pair("member_a", 85), Pair("member_b", 72), Pair("member_c", 91)]

// zip with transform — カスタム変換します
val results = names.zip(scores) { name, score -> "$name: $score点" }

// unzip — Pair のリストを2つのリストに分解します
val (n, s) = zipped.unzip() // names, scores に戻ります

メソッド一覧

メソッド概要
リストA.zip(リストB)2つのリストを Pair のリストに結合します。短い方の長さに合わせます。
リストA.zip(リストB) { a, b -> 変換 }変換ラムダを指定して zip します。
ペアリスト.unzip()Pair<A, B> のリストを Pair<List<A>, List<B>> に分解します。
リスト.zipWithNext()隣り合う要素のペアのリストを返します(差分計算などに使います)。
リスト.zipWithNext { a, b -> }隣り合う要素を変換してリストを返します。

サンプルコード

sample_list_zip_unzip.kt
fun main() {
    // 基本の zip
    val names = listOf("member_a", "member_b", "member_c", "member_d")
    val scores = listOf(85, 72, 91, 68)
    val ages = listOf(20, 20, 24) // 短い方に合わせます(3件)
    val tags = listOf("value_1", "value_2", "value_3", "value_4")

    val zipped = names.zip(scores)
    println("zip: $zipped")

    // zip with transform(変換付き)
    val results = names.zip(scores) { name, score ->
        "$name: ${score}点 (${if (score >= 80) "合格" else "不合格"})"
    }
    results.forEach { println(it) }

    println()

    // 長さが異なる場合(短い方に合わせます)
    val nameAge = names.zip(ages)
    println("長さ違い zip: $nameAge") // 3件

    println()

    // 名前とタグの zip
    val nameTagPairs = names.zip(tags) { name, tag -> "$name → $tag" }
    nameTagPairs.forEach { println(it) }

    println()

    // unzip — Pair を2つのリストに分解します
    val (nameList, scoreList) = zipped.unzip()
    println("名前: $nameList")
    println("点数: $scoreList")

    println()

    // zipWithNext — 隣接要素のペアを作ります(差分計算)
    val temps = listOf(20.0, 22.5, 19.0, 25.0, 23.0)
    println("気温変化:")
    temps.zipWithNext { today, tomorrow ->
        val diff = tomorrow - today
        val dir = if (diff > 0) "↑" else if (diff < 0) "↓" else "→"
        println("  ${today}℃ $dir ${tomorrow}℃ (${"%.1f".format(diff)})")
    }

    println()

    // 3つのリストを zip(2回のzipで対応)
    val first = listOf(1, 2, 3)
    val second = listOf("a", "b", "c")
    val third = listOf(true, false, true)
    val triple = first.zip(second).zip(third) { (a, b), c -> Triple(a, b, c) }
    triple.forEach { println(it) }
}
list_zip_unzip.kt
kotlinc list_zip_unzip.kt -include-runtime -d list_zip_unzip.jar
java -jar list_zip_unzip.jar
zip: [(member_a, 85), (member_b, 72), (member_c, 91), (member_d, 68)]
member_a: 85点 (合格)
member_b: 72点 (不合格)
member_c: 91点 (合格)
member_d: 68点 (不合格)

長さ違い zip: [(member_a, 20), (member_b, 20), (member_c, 24)]

member_a → value_1
member_b → value_2
member_c → value_3
member_d → value_4

名前: [member_a, member_b, member_c, member_d]
点数: [85, 72, 91, 68]

気温変化:
  20.0℃ ↑ 22.5℃ (2.5)
  22.5℃ ↓ 19.0℃ (-3.5)
  19.0℃ ↑ 25.0℃ (6.0)
  25.0℃ ↓ 23.0℃ (-2.0)

(1, a, true)
(2, b, false)
(3, c, true)

よくあるミス

『zip』の要素数の扱い、『unzip』の戻り値の型、『zipWithNext』と手動インデックス計算の違いに関してよくあるミスをまとめます。

ミス原因と対処
『zip』が長い方のリストの要素も全て含まれると思う『zip』は短い方のリストに合わせて切り捨てる(例外は発生しない)。長い方の余剰要素は無視される。
『unzip』の戻り値の型を忘れる『unzip』は『Pair<List<A>, List<B>>』を返す。分割宣言『val (names, scores) = pairs.unzip()』で受け取ると意図が明確になる。
『zipWithNext』を『zip』と混同してインデックスを自分で計算しようとする『zipWithNext』は同じリスト内の隣接する2要素のペアを作る。引数に別リストは不要で、手動インデックス計算より簡潔に書ける。

下記のサンプルコードで、正しい使い方を確認できます。

sample_list_zip_unzip_mistakes.kt
fun main() {
    val names = listOf("member_a", "member_b", "member_c", "member_d")
    val scores = listOf(85, 72, 91)

    val zipped = names.zip(scores)
    println("zip 結果の件数: ${zipped.size}") // 3(4件あっても3件)
    println("zip: $zipped")

    // unzip は first でも可、分割宣言の方がわかりやすい
    val unzipped = zipped.unzip()
    println("first: ${unzipped.first}")
    val (nameList, scoreList) = zipped.unzip()
    println("名前: $nameList")
    println("スコア: $scoreList")

    // zipWithNext を使う方が簡潔
    val temps = listOf(20.0, 22.5, 19.0, 25.0)
    for (i in 0 until temps.size - 1) {
        println("手動差分: ${temps[i + 1] - temps[i]}")
    }
    temps.zipWithNext { a, b -> println("zipWithNext差分: ${b - a}") }
}

コンパイルして実行すると次のようになります。

kotlinc sample_list_zip_unzip_mistakes.kt -include-runtime -d sample_list_zip_unzip_mistakes.jar
java -jar sample_list_zip_unzip_mistakes.jar
zip 結果の件数: 3
zip: [(member_a, 85), (member_b, 72), (member_c, 91)]
first: [member_a, member_b, member_c]
名前: [member_a, member_b, member_c]
スコア: [85, 72, 91]
手動差分: 2.5
手動差分: -3.5
手動差分: 6.0
zipWithNext差分: 2.5
zipWithNext差分: -3.5
zipWithNext差分: 6.0

概要

『zip()』は2つのリストを対応させて処理するときに便利です。長さが異なる場合は短い方に合わせて切り捨てられます(例外は発生しません)。

『zipWithNext()』は連続する2要素の差分や変化率を計算するときに使います。for ループで添字を使う処理の代わりになります。

リストの平坦化はリスト — flatMap() / flatten()を、畳み込みはリスト — reduce() / fold()を参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。