将键映射到匹配列表列表的惯用方式是什么?一个例子-给出:
val s = listOf(1, 9)
val u = listOf(listOf(1, 2, 3), listOf(1, 4, 7), listOf(1, 5, 9))
我想要一个Map<Int, List<List<Int>>>
,以便将s
中的每个键都映射到包含该键的列表的列表:
{1=[ [1, 2, 3], [1, 4, 7], [1, 5, 9] ], 9=[ [1, 5, 9] ]}
以下内容:
s.groupBy({ it }, { x -> u.filter { it.contains(x) } })
产生:
{1=[[[1, 2, 3], [1, 4, 7], [1, 5, 9]]], 9=[[[1, 5, 9]]]}
这不太正确,也不清楚如何将结果展平为预期的形状。
答案 0 :(得分:2)
我会推荐associateWith
并像这样使用它:
s.associateWith { num -> u.filter { list -> num in list } }
输出:
{1 = [[1、2、3],[1、4、7],[1、5、9]],9 = [[1、5、9]]}
我最初建议使用associate
,但是如果您使用associateWith
,则可以进一步缩短代码。感谢Abhay Agarwal的推荐。
答案 1 :(得分:1)
更新
您只需要flatten
的值Map
。
val w = s.groupBy({ it }, { x -> u.filter { it.contains(x) } })
.mapValues { it.value.flatten() }
我的解决方案map
是第一个集合,它将每个元素与出现的列表配对,然后groupBy
将结果列表配对。
示例
val w = s.map { elem -> Pair(elem, u.filter { list -> elem in list }) }
.groupBy ({ it.first }, { it.second })
.mapValues { it.value.flatten() }
check(w[1] == listOf(listOf(1, 2, 3), listOf(1, 4, 7), listOf(1, 5, 9)))
check(w[9] == listOf(listOf(1, 5, 9)))
println(w)
输出
{1=[[1, 2, 3], [1, 4, 7], [1, 5, 9]], 9=[[1, 5, 9]]}
答案 2 :(得分:0)
对我来说,s.groupBy(....)
是惯用法,@ Omar Mainegra的回答-s.groupBy(...).mapValues( flatten )
绝对有效,但它看起来像hack,最初的结果需要一些额外的按摩。
问题出在groupBy
的实现上,更具体地说是groupByTo
的实现:
public inline fun <T, K, V, M : MutableMap<in K, MutableList<V>>> Iterable<T>.groupByTo(destination: M, keySelector: (T) -> K, valueTransform: (T) -> V): M {
for (element in this) {
val key = keySelector(element)
val list = destination.getOrPut(key) { ArrayList<V>() }
list.add(valueTransform(element))
}
return destination
}
该实现将与键相关联的值包装在列表中,因为通常可以将多个值与键相关联,在这里不是这种情况
s
中的值是唯一的,这意味着groupBy
是使用错误的函数。正确的功能是associateWith
:
s.associateWith { x -> u.filter { it.contains(x) } }
产生:
{1=[[1, 2, 3], [1, 4, 7], [1, 5, 9]], 9=[[1, 5, 9]]}