我有两个不同类型的未分类的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)
答案 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,这可能会有一些奇怪的行为,并且根据流的结构,这可能最终会缓冲内存中的大量元素。