使用 RxJava 生成一个映射,其中键是 Kotlin 枚举的值,映射的值来自另一个 RxJava 流

Posted

技术标签:

【中文标题】使用 RxJava 生成一个映射,其中键是 Kotlin 枚举的值,映射的值来自另一个 RxJava 流【英文标题】:Using RxJava to generate a map where keys are values of a Kotlin enum and map's values come from another RxJava stream 【发布时间】:2021-10-08 00:29:34 【问题描述】:

简介

假设我有一个 Kotlin 枚举类:

enum class Type 
    ONE,
    TWO,
    THREE,
    FOUR

以及以下数据类:

data class Item(
    val name: String,
    val type: Type
)

然后我有一个Single,它发出一个Items 的列表——可以通过任何方式,但出于示例目的,假设它看起来像这样:

val itemsSingle = Single.just(listOf(
    Item("A", Type.ONE),
    Item("B", Type.ONE),
    Item("C", Type.TWO),
    Item("D", Type.THREE),
))

问题

我想要实现的是拥有一个 RxJava 流,它将输出一个映射,其中键来自 Type,值是与给定 Type 值匹配的 Items 列表(其中未确定的、未排序的Items 的列表由其他一些 Single 流提供)。签名将是:

Single<Map<Type, List<Item>> // or Observable<Map<Type, List<Item>>

另一个要求是映射的键应始终耗尽 Type 枚举中的所有值,即使 itemsSingle 流不包含某些 Type 值的项目(或根本没有项目)。因此,对于提供的 itemsSingle 示例流,返回的映射应如下所示:


    ONE:   [ Item(name: "A", type: ONE), Item(name: "B", type: ONE) ],
    TWO:   [ Item(name: "C", type: TWO) ],
    THREE: [ Item(name: "D", type: THREE) ],
    FOUR:  []

尝试

通过以上所有步骤,我已经通过以下步骤达到了预期的结果:

    为了满足用尽所有 Type 枚举值的要求,我首先创建一个映射,其中包含所有可能的 Type 值的空列表:
val typeValuesMap = Type.values().associate  it to emptyList<Item>() 
val typeValuesMapSingle = Single.just(typeValuesMap)

// result: ONE=[], TWO=[], THREE=[], FOUR=[]
    我可以得到一张地图,其中包含来自 itemsSingle 的项目,这些项目分组在各自的 Type 值键下:
val groupedItemsMapSingle = itemsSingle.flattenAsObservable  it 
    .groupBy  it.type 
    .flatMapSingle  it.toList() 
    .toMap  list -> list[0].type  // the list is guaranteed to have at least one item

// result: ONE=[Item(name=A, type=ONE), Item(name=B, type=ONE)], THREE=[Item(name=D, type=THREE)], TWO=[Item(name=C, type=TWO)]
    最后我可以使用 combineLatest 运算符合并两个列表,并覆盖给定 Type 值的初始空项目列表:
Observable.combineLatest(
    typeValuesMapSingle.flattenAsObservable  it.entries ,
    groupedItemsMapSingle.flattenAsObservable  it.entries 
)  a, b -> listOf(a, b) 
    .defaultIfEmpty(typeValuesMap.entries.toList()) // in case itemsSingle is empty
    .flatMapIterable  it 
    .collect(mutableMapOf<Type, List<Item>>(),  a, b -> a[b.key] = b.value)

// result: FOUR=[], ONE=[Item(name=A, type=ONE), Item(name=B, type=ONE)], THREE=[Item(name=D, type=THREE)], TWO=[Item(name=C, type=TWO)]

总结

如您所见,对于一个看似简单的操作,它的代码量相当多。所以我的问题是——有没有更简单的方法来达到我想要的结果?

【问题讨论】:

【参考方案1】:

只需将空列表映射与填充列表映射合并

val result = itemsSingle.map  items->
   Type.values().associateWith  listOf<Item>()  + items.groupBy  it.type 

【讨论】:

以上是关于使用 RxJava 生成一个映射,其中键是 Kotlin 枚举的值,映射的值来自另一个 RxJava 流的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Retrofit2,RxJava2,Gson TypeAdapterFActory正确映射Gson?

父表的主键是子表的主键,如何使用jpa进行映射?

NHibernate的主键是1-1映射吗?

为什么在哈希映射中使用空值或空键是有用的?

Go教程映射

Go教程映射