Группировка тех же соседей с помощью Kotlin операций по сбору - PullRequest
0 голосов
/ 27 марта 2020

Какая операция из Kotlin Коллекции будет наиболее подходящей для группировки в списке тех же последовательных соседей? Допустим, у нас есть список:

['A', 'A', 'B', 'B', 'B', 'A', 'B']

, и я хотел бы получить список групп, разделенных при изменении элемента (с сохранением порядка):

[['A', 'A'], ['B', 'B', 'B'], ['A'], ['B']]

Вроде бы распространенный случай, поэтому я подумал, что он должен поддерживаться богатыми операциями Kotlin Collections, но я не нашел там ничего подходящего.

Я уже пробовал использовать reduce, fold и кажется, что все это вообще не касается этого случая. Я думаю, что использование groupBy довольно близко к желаемому результату:

val chars: List<Char> = "AABBBAB".toList()
val groups = chars.groupBy { char -> char }
    .map { (key, grouped) ->
         grouped
    }

Это работает почти хорошо, но объединяет одни и те же типы групп: [['A', 'A', 'A'], ['B', 'B', 'B', 'B']])

Я знаю, как это сделать что используются некоторые уродливые циклы и буферы, но мой вопрос заключается в том, как этого добиться, используя некоторые необычные Kotlin операции (или их комбинацию)?

Мой реальный случай немного сложнее (я бы хотел сгруппировать некоторые объекты по своему свойству способом, описанным выше).

Ответы [ 2 ]

1 голос
/ 27 марта 2020

Складывать это путь к go:

val source = listOf('A', 'A', 'B', 'B', 'B', 'A', 'B')

source.fold(mutableListOf<MutableList<Char>>()) { sum, c ->
     if (sum.isEmpty() || sum.last().first() != c) {
         // add new group
         sum.add(mutableListOf(c))
     } else {
         // add to the last group
         sum.last().add(c)
     }
     sum
 }
1 голос
/ 27 марта 2020

Лучшее, что я придумал, использует MutableLists, которые вы могли бы описать как «некрасивые циклы и буферы».

fun <T, C> Iterable<T>.separateConsecutiveDuplicates(comparisonSelector: (T) -> C): List<List<T>> =
    mutableListOf<Pair<C, MutableList<T>>>().also { lists ->
        forEach {
            val comparison = comparisonSelector(it)
            if (lists.isEmpty() || lists.last().first != comparison)
                lists += comparison to mutableListOf(it)
            else
                lists.last().second += it
        }
    }.map { it.second }

Если вы не возражаете, селектор запускается по каждому элементу дважды Код немного чище:

fun <T, C> Iterable<T>.separateConsecutiveDuplicates(comparisonSelector: (T) -> C): List<List<T>> =
    mutableListOf<MutableList<T>>().also { lists ->
        forEach {
            if (lists.isEmpty() || comparisonSelector(lists.last().last()) != comparisonSelector(it) )
                lists += mutableListOf(it)
            else
                lists.last() += it
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...