Группировка элементов коллекции

В стандартной библиотеке Kotlin есть функции-расширения для группировки элементов коллекции. Основная функция для группировки - groupBy(), которая принимает лямбда-функцию и возвращает Map. В этой Map каждый ключ — это результат вычисления лямбда-функции, а соответствующее ему значение — это List из элементов, значения которых сходятся с полученным результатом от лямбда-функции. Функцию groupBy() можно использовать, например, для группировки списка строк по их первой букве.

Также groupBy() может быть передана вторая лямбда-функция — функция преобразования значений. В этом случае ключи, созданные первой лямбдой keySelector, будут отражать результаты второй лямбды valueTransform, вместо исходных элементов.

fun main() {
    val numbers = listOf("one", "two", "three", "four", "five")

    println(numbers.groupBy { it.first().uppercase() }) // {O=[one], T=[two, three], F=[four, five]}

    println(numbers.groupBy(
      keySelector = { it.first() },
      valueTransform = { it.uppercase() }
    )) // {o=[ONE], t=[TWO, THREE], f=[FOUR, FIVE]}
}

Если вам требуется сгруппировать элементы, а затем применить какую-либо операцию ко всем группам одновременно, то используйте функцию groupingBy(). Она возвращает экземпляр типа Grouping. В свою очередь Grouping позволяет "лениво" применять операции ко всем группам: фактически группы будут создаваться прямо перед выполнением операции.

Grouping поддерживает следующие операции:

  • eachCount() - подсчитывает количество элементов в каждой группе.
  • fold() и reduce() - выполняют операции fold и reduce для каждой группы как для отдельной коллекции, после чего возвращают результат.
  • aggregate() - последовательно применяет данную операцию ко всем элементам в каждой группе, после чего возвращает результат. Это общий способ выполнения любых операций с объектом Grouping. Используйте его для реализации собственных операций, когда операций fold и reduce недостаточно.
fun main() {
    val numbers = listOf("one", "two", "three", "four", "five", "six")
    println(numbers.groupingBy { it.first() }.eachCount()) // {o=1, t=2, f=2, s=1}
}