使用 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
简介
假设我有一个 Kotlin 枚举 class:
enum class Type {
ONE,
TWO,
THREE,
FOUR
}
和以下数据class:
data class Item(
val name: String,
val type: Type
)
然后我有一个 Single
发出一个 Item
的列表 – 可以通过任何方式但为了举例目的,假设它看起来像这样:
val itemsSingle = Single.just(listOf(
Item("A", Type.ONE),
Item("B", Type.ONE),
Item("C", Type.TWO),
Item("D", Type.THREE),
))
问题
我想要实现的是拥有一个 RxJava 流,它将输出一个映射,其中键来自 Type
,值是 Item
列表匹配给定的 Type
值(其中 Item
的未确定、未排序的列表由其他 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
值的初始空项目列表 if itemsSingle
包含任何 Item
s 对于此 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)]}
总结
如您所见,看似简单的操作代码却不少。所以我的问题是 – 是否有更简单的方法来实现我想要的结果?
只需将空列表映射与填充列表映射合并
val result = itemsSingle.map { items->
Type.values().associateWith { listOf<Item>() } + items.groupBy { it.type }
}
简介
假设我有一个 Kotlin 枚举 class:
enum class Type {
ONE,
TWO,
THREE,
FOUR
}
和以下数据class:
data class Item(
val name: String,
val type: Type
)
然后我有一个 Single
发出一个 Item
的列表 – 可以通过任何方式但为了举例目的,假设它看起来像这样:
val itemsSingle = Single.just(listOf(
Item("A", Type.ONE),
Item("B", Type.ONE),
Item("C", Type.TWO),
Item("D", Type.THREE),
))
问题
我想要实现的是拥有一个 RxJava 流,它将输出一个映射,其中键来自 Type
,值是 Item
列表匹配给定的 Type
值(其中 Item
的未确定、未排序的列表由其他 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
值的初始空项目列表 ifitemsSingle
包含任何Item
s 对于此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)]}
总结
如您所见,看似简单的操作代码却不少。所以我的问题是 – 是否有更简单的方法来实现我想要的结果?
只需将空列表映射与填充列表映射合并
val result = itemsSingle.map { items->
Type.values().associateWith { listOf<Item>() } + items.groupBy { it.type }
}