Списки: идиоматический способ фильтрации похожих элементов



Я ищу противоположность distinct.



В списке с [ "a", "a", "b", "c", "b", "d" ] я хочу только сохранить "a" и "b", потому что они появляются несколько раз.



Решением для этого может быть следующее:



val similarsList = mutableListOf<String>()
list.filter {
if (similars.contains(it))
return@filter true
similars.add(it)
false
}.distinct()


Это приведет к удалению каждого элемента, который соответствует уже содержащемуся элементу до него. В объекте list все элементы, появляющиеся несколько раз, будут сохранены после filter. distinct в этом случае очищает элементы, появившиеся три и более раз.



Я я ищу идиоматический способ делать именно это, противоположный distinct.

601   2  

2 ответов:

Небольшое улучшение к решению, предоставленному @gil.fernandes можно сделать с помощью API, введенного в стандартной библиотеке Kotlin 1.1: .groupingBy { ... }. Вместо того, чтобы сразу создавать группы, он создает ленивый Grouping, который затем может быть опрошен. В частности, мы можем попросить его .eachCount(), который возвращает Map<T, Int>:

val l = listOf("a", "a", "b", "c", "b", "d")

val result = l.groupingBy { it }.eachCount().filterValues { it > 1 }.keys

Это немного эффективнее, чем использование .groupBy { ... }, потому что он не хранит группы в списках.

Если вы хотите отфильтровать дубликаты (не отдельные элементы), вы можете попробовать:

fun main(args: Array<String>) {
    val l = listOf( "a", "a", "b", "c", "b", "d" )
    println(l.groupBy { it }.filter { it.value.size > 1 }.keys)
}

Это выводит:

[a, b]

Пояснение: l.groupBy { it } создает сгруппированную карту с таким содержанием:

{a=[a, a], b=[b, b], c=[c], d=[d]}
Затем к этой карте применяется фильтр, фильтрующий по значениям (которые являются списками) с длиной, превышающей 1: filter { it.value.size > 1 }.

Наконец, ключи извлекаются с помощью keys.

Comments

    Ничего не найдено.