Alex Facciorusso
Alex Facciorusso

Reputation: 2408

Kotlin combine two flows depending from each other

I have a data class, MyThing1, which has an ID in it. data class MyThing1(val id: String) I have another data class which we don’t care about its content, but let’s call it MyThing2 I have a function that returns a list of MyThing1

fun myEmitter1(): Flow<List<MyThing1>>

I have another Flow that takes in input an ID and returns a flow of objects, so

fun myEmitter2(id: String): Flow<MyThing2>

For each MyThing1 received from the flow, I want to combine it with the latest MyThing2 that gets emitted by myEmitter2 and return a Flow<List<CombinedThing>> given the CombinedThing is

data class CombinedThing(val myThing1: MyThing1, val myThing2: MyThing2) .

and where every times myThing1 changes, it observes myEmitter2(id) and every time myThing2 emits, it emits a CombinedThing.

In a diagram, I'd like something like this:

[mt11,mt12]--------------------------------------------------------------------------
observe for id 1 ----mt211-----------------------------------------------------------
--[observe for id 2]------------------------mt212-------------------------------------

---------------------[CT(mt11,mt211(1))]----[CT(mt11,mt211(1)),CT(mt2,mt212)]---------

Upvotes: 4

Views: 5419

Answers (1)

Sam
Sam

Reputation: 10006

So the inputs to myEmitter2 should be the IDs of the values from myEmitter1?

We could implement this with the flatMapLatest and combine operators:

myEmitter1().flatMapLatest { myThing1s: List<MyThing1> ->
    val myThing2s: List<Flow<Pair<MyThing1 to MyThing2>> = myThing1s.map { t1 ->
        myEmitter2(t1.id).map { t2 -> t1 to t2 }
    }
    combine(myThing2s) { combined: Array<Pair<MyThing1, MyThing2>> ->
        combined.map { (t1, t2) -> CombinedThing(t1, t2) }
    }
}

From the docs, flatMapLatest creates "a flow that switches to a new flow produced by transform function every time the original flow emits a value". That fits your requirement that "every time myThing1 changes, it observes myEmitter2(id)".

Inside flatMapLatest's transform function, we see each list emitted by myEmitter1. We use map to turn the List<MyThing1> into a list of flows. Each flow will take the values from myEmitter2 and use map (again) to pair them up with the original myThing1 value. This gives us a List<Flow<Pair<MyThing1 to MyThing2>>.

Finally, we use combine to turn the list of flows into a Flow<List<CombinedThing>>. The combine operator takes a list of flows and emits values that are computed by combining the most recently emitted values from each flow. We get an array (I've called it combined) containing the most recent value from each of our flows. Using map, we can turn that array into a List and turn our pair into a CombinedThing object.

Upvotes: 7

Related Questions