0Pricing
Kotlin Academy · レッスン

combineとzip:複数のFlowを統合する

複数のFlowをcombineし、発行内容をzipでまとめます。

「combineとzip:複数のFlowを統合する」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

フローを結合する理由

実際のアプリでは、複数のデータストリームを結合することがよくあります。たとえば、ユーザー設定とネットワークデータ、フォームフィールドとバリデーション状態などです。Kotlin Flowには、そのためのzip、combine、mergeが用意されています。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val names = flowOf("Alice", "Bob", "Carol")
    val ages  = flowOf(30, 25, 35)
    names.zip(ages) { name, age -> "$name is $age" }
         .collect { println(it) }
}

zip:対応する値の発行

zipは、値の発行を順番どおりに1対1で対応付けます。短い方のフローが終了すると停止します。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val a = flowOf(1, 2, 3, 4)   // 4 elements
    val b = flowOf("a", "b", "c")  // 3 elements — zip stops here
    a.zip(b) { n, s -> "$n-$s" }
     .collect { println(it) } // 1-a, 2-b, 3-c
}

combine:最新値

combineは、フローのいずれかが値を発行するたびに、各フローの最新値を使って新しい値を発行します。両方のフローが少なくとも1回は値を発行している必要があります。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val flow1 = MutableStateFlow(1)
    val flow2 = MutableStateFlow("a")
    combine(flow1, flow2) { n, s -> "$n-$s" }
        .take(1)
        .collect { println(it) } // 1-a
}

combineとzipの違い

zipは対応するペアを待ちます。combineは最新値を使って更新のたびに処理します。リアクティブUI(たとえば2つのフォームフィールド)にはcombineを、順番に対応付ける場合にはzipを使用します。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    // zip: 1-a, 2-b
    val nums = flowOf(1, 2).zip(flowOf("a", "b")) { n, s -> "$n-$s" }
    nums.collect { println("zip: $it") }

    // combine fires more:
    combine(flowOf(1, 2), flowOf("a")) { n, s -> "$n-$s" }
        .collect { println("combine: $it") }
}

merge:フローのインターリーブ

mergeは、複数のフローから値が到着した順に、対応付けを行わず発行します。出力順序は値が発行されるタイミングによって決まります。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val a = flow { emit("A1"); delay(50); emit("A2") }
    val b = flow { delay(25); emit("B1") }
    merge(a, b).collect { println(it) } // A1, B1, A2
}

3つ以上のフローでcombineを使用する

combineは複数のフローに対応しています。フローをvarargとして渡すと、ラムダにはすべての最新値が渡されます。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val a = MutableStateFlow("hello")
    val b = MutableStateFlow(42)
    val c = MutableStateFlow(true)
    combine(a, b, c) { s, n, flag -> "$s-$n-$flag" }
        .take(1)
        .collect { println(it) } // hello-42-true
}

flatMapMerge:内部フローの並行処理

flatMapMergeは各要素をフローに変換し、すべての内部フローを並行して収集しながら、その出力を結合します。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    (1..3).asFlow()
        .flatMapMerge { n ->
            flow { delay(100L - n * 10); emit("result-$n") }
        }
        .collect { println(it) }
}

flatMapLatest:新しい値でキャンセル

flatMapLatestは、上流から新しい要素が到着すると現在の内部フローをキャンセルします。入力中の検索に最適です。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flowOf("k", "ko", "kot", "kotl", "kotli", "kotlin")
        .flatMapLatest { query ->
            flow {
                delay(50)  // simulate debounced search
                emit("Result for: $query")
            }
        }
        .collect { println(it) } // only "Result for: kotlin"
}

トランザクションの対応付けにzipを使用する

zipを使用して2つのストリームからのイベントを対応付けます。たとえば、ユーザー操作とサーバー確認を対応付けることで、1対1の対応関係を保証できます。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val orders = flowOf("Order1", "Order2", "Order3")
    val confirmations = flowOf("Confirmed", "Confirmed", "Rejected")
    orders.zip(confirmations) { order, status -> "$order: $status" }
          .collect { println(it) }
}

フォームバリデーションにcombineを使用する

ユーザー名とパスワードのフローをcombineして、フォームの有効性をリアクティブに計算します。どちらかのフィールドが変更されるたびに更新されます。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val username = MutableStateFlow("")
    val password = MutableStateFlow("")
    val isValid = combine(username, password) { u, p ->
        u.length >= 3 && p.length >= 8
    }
    username.value = "alice"
    password.value = "secure123"
    println(isValid.first()) // true
}

複数ソースのローディングにonEach + combineを使用する

ローディングフラグとデータフローをcombineして、複数の独立したソースから単一のUI状態オブジェクトを生成します。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
data class UI(val loading: Boolean, val data: String)
fun main() = runBlocking {
    val loading = MutableStateFlow(true)
    val data    = MutableStateFlow("")
    combine(loading, data) { l, d -> UI(l, d) }
        .take(2)
        .collect { println(it) }
    data.value = "result"
    loading.value = false
}

クイックチェック

zipとcombineの主な違いは何ですか。

復習

zipは値の発行を順番に対応付けます。combineは最新値を使って、いずれかの更新時に処理します。mergeは独立したフローをインターリーブします。flatMapLatestは古くなった内部フローをキャンセルします。対応付けが必要か、リアクティブな更新が必要かに応じて選択します。

よくある質問

「combineとzip:複数のFlowを統合する」レッスンは無料ですか?

はい。「combineとzip:複数のFlowを統合する」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「combineとzip:複数のFlowを統合する」で何を学びますか?

複数のFlowをcombineし、発行内容をzipでまとめます。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「combineとzip:複数のFlowを統合する」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Flow演算子:map、filter、transform、take
  2. catchとonCompletion:Flowのエラー処理
  3. combineとzip:複数のFlowを統合する
  4. flowOnとbuffer:コンテキストとバックプレッシャー
← Kotlin Academyに戻る