Списки: идиоматический способ фильтрации похожих элементов
Я ищу противоположность 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.
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 }создает сгруппированную карту с таким содержанием:Затем к этой карте применяется фильтр, фильтрующий по значениям (которые являются списками) с длиной, превышающей 1:{a=[a, a], b=[b, b], c=[c], d=[d]}filter { it.value.size > 1 }.Наконец, ключи извлекаются с помощью
keys.
Comments