将键集映射到匹配的列表列表

时间:2018-12-31 20:42:44

标签: kotlin

将键映射到匹配列表列表的惯用方式是什么?一个例子-给出:

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]]]}         

这不太正确,也不清楚如何将结果展平为预期的形状。

3 个答案:

答案 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]]}