如何通过密钥加入两个RxJava2 Obvervables?

时间:2017-08-14 09:29:44

标签: java kotlin rx-java2

我有两个不同类型的未分类的observable。这两种类型共享一个共同的密钥。我想将它们加入到一个新的可观察的发射对的相应元素中,我无法弄清楚如何去做。

请注意,某些键可能会丢失。如果没有丢弃完整的对,那就没关系了,但是用null代替丢失的部分会更好。

输入1:

Entity(id = 2),
Entity(id = 1),
Entity(id = 4)

输入2:

Dto(id = 3),
Dto(id = 2),
Dto(id = 1)

预期输出(以任何顺序):

Pair(Entity(id = 1), Dto(id = 1)),
Pair(Entity(id = 2), Dto(id = 2)),
Pair(null, Dto(id = 3)),
Pair(Entity(id = 4), null)

1 个答案:

答案 0 :(得分:1)

首先,将Observable.merge个流放在一起:这会为您提供所有项目的流。 (在下面的代码中,我使用了自定义Either类来标记每个流。)

然后,对于流中的每个项目,尝试将其与先前观察到的其他类型的项目匹配,并输出该对。如果没有,请将其保存以便以后匹配。

最后,一旦完成流,剩余的不匹配元素将不会与任何内容匹配,因此它们可以不成对发射。

import io.reactivex.Observable

data class Entity(val id: Int)
data class Dto(val id: Int)

sealed class Either<out A, out B>
data class Left<A>(val value: A) : Either<A, Nothing>()
data class Right<B>(val value: B) : Either<Nothing, B>()

fun <A : Any, B : Any, C> joinById(a: Observable<A>, idA: (A) -> C, b: Observable<B>, idB : (B) -> C): Observable<Pair<A?, B?>> {
    val unmatchedA = mutableMapOf<C, A>()
    val unmatchedB = mutableMapOf<C, B>()
    val merged = Observable.mergeDelayError(a.map(::Left), b.map(::Right)).flatMap { latest ->
        when (latest) {
            is Left -> {
                val id = idA(latest.value)
                unmatchedB.remove(id)?.let { return@flatMap Observable.just(latest.value to it) }
                unmatchedA.put(id, latest.value)
            }
            is Right -> {
                val id = idB(latest.value)
                unmatchedA.remove(id)?.let { return@flatMap Observable.just(it to latest.value) }
                unmatchedB.put(id, latest.value)
            }
        }
        Observable.empty<Nothing>()
    }
    return Observable.concat(merged, Observable.create { emitter ->
        unmatchedA.values.forEach { emitter.onNext(it to null) }
        unmatchedB.values.forEach { emitter.onNext(null to it) }
        emitter.onComplete()
    })
}

fun main(args: Array<String>) {
    val entities = Observable.just(Entity(2), Entity(1), Entity(4))
    val dtos = Observable.just(Dto(3), Dto(2), Dto(1))
    joinById(entities, Entity::id, dtos, Dto::id).blockingForEach(::println)
}
(Entity(id=2), Dto(id=2))
(Entity(id=1), Dto(id=1))
(Entity(id=4), null)
(null, Dto(id=3))

请注意,如果在流中重复ID,这可能会有一些奇怪的行为,并且根据流的结构,这可能最终会缓冲内存中的大量元素。