Группировать коллекцию по условиям - PullRequest
0 голосов
/ 11 января 2019

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

Например:

class Item(val level : Int)

С учетом списка: (Item(1), Item(2), Item(5))

и два условия группировки:

  1. level > 0 && level < 3
  2. level > 4

Ожидаются следующие списки:

  1. listOf(Item(1), Item(2))
  2. listOf(Item(5))

Функция groupBy принимает только 1 аргумент условия. Есть ли другая функция, которая будет полезна?

Ответы [ 3 ]

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

Попробуйте filter всех элементов, которые не нужны, а затем либо groupBy, либо partition их, например:

с использованием partition (т.е. вам нужно только 2 списка из 1):

listSequence()
  .filter { it.level > 0 && it.level != 3 } // it seems you are only interested in levels > 0 && != 3
  .partition { it.level in 1..2 } // partition into items having 0 < level < 3 and the rest
  .run(::println) // prints: [[Item(level=1), Item(level=2)], [Item(level=5)]] (which is a pair of lists)

с использованием groupBy аналогично тому, что показал Вилли Ментцель:

listSequence()
    .filter { it.level > 0 && it.level != 3 } // if you need to filter... otherwise skip that and assign just a random group
    .groupBy { 
       when (it.level) {
         in 1..2 -> 0
         else -> 1
       }
    }
    .values.run(::println) // which also prints ([Item(level=1), Item(level=2)], [Item(level=5)]) but now is a collection of lists

В обоих случаях я использовал следующую последовательность:

fun listSequence() = sequenceOf(Item(1), Item(2), Item(5), Item(-4), Item(0))

Зависит от того, что вы хотите достичь в конце ... Вас также могут заинтересовать некоторые другие доступные функции сбора .

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

Я бы также использовал partion, как уже предлагалось, здесь. Вы также можете связать их в цепочку:

val cond1: (Item) -> Boolean = { it.level in 0..2 }
val cond2: (Item) -> Boolean = { it.level > 4 }
val parts = elements
    .partition { cond1(it) || cond2(it) }
    .first.partition { cond1(it) }
println(parts)

Это приведет к итерациям вашего ввода, которые немного менее эффективны, чем groupBy. Все еще линейная сложность во время выполнения.

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

Вы можете вернуть Int в лямбду, переданной groupBy, которая идентифицирует ваши критерии. Это будет работать для любого количества условий.

val l = listOf(Item(1), Item(2), Item(5))

val g = l.groupBy {
    when {
        it.level > 0 && it.level < 3 -> 0
        it.level > 4 -> 1
        // ...
        else -> null
    }
}.filterKeys { it != null }) // optional: filter out null as default key

Результат:

{0 = [Предмет (уровень = 1), Предмет (уровень = 2)], 1 = [Предмет (уровень = 5)]}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...