Сопоставление набора ключей с соответствующим списком списков - PullRequest
0 голосов
/ 31 декабря 2018

Что такое идиоматический способ сопоставления ключей с соответствующим списком списков?Пример, приведенный ниже:

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 голосов
/ 01 января 2019

Для меня идиоматично было бы s.groupBy(....) Ответ @Omar Mainegra - s.groupBy(...).mapValues( flatten ) абсолютно работает, но выглядит как хак, где первоначальный результат требует дополнительного массажа.

Проблема связана с реализацией 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]]}    
0 голосов
/ 01 января 2019

Я бы порекомендовал 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.Спасибо Абхай Агарвал , который порекомендовал его.

0 голосов
/ 01 января 2019

Обновление

Вам просто нужно 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]]}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...