Reputation: 21
In Kotlin, what would be a neat (preferably functional) way of grouping elements having n
grouping conditions?
For example:
class Item(val level : Int)
Given the list: (Item(1), Item(2), Item(5))
and two grouping conditions:
level > 0 && level < 3
level > 4
The following lists are expected:
listOf(Item(1), Item(2))
listOf(Item(5))
The groupBy
function takes only 1 condition argument. Is there any other function that would be helpful?
Upvotes: 2
Views: 1302
Reputation: 23262
Try to filter
out all elements which aren't necessary and then either groupBy
or partition
them, e.g.:
using partition
(i.e. you only need 2 lists out of 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)
using groupBy
similar to what Willi Mentzel has shown:
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
In both cases I used a sequence as follows:
fun listSequence() = sequenceOf(Item(1), Item(2), Item(5), Item(-4), Item(0))
Depends what you want to accomplish in the end... You may also be interested in some of the other available collection functions.
Upvotes: 0
Reputation: 81929
I'd also utilize partion
, as already suggested, here. You could also chain them:
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)
This will result into iterations of your input which is slightly less efficient than the groupBy
. Still a linear runtime complexity.
Upvotes: 0
Reputation: 29844
You could return an Int
in the lambda passed to groupBy
which identifies your criteria. This would work for any number of conditions.
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
Result:
{0=[Item(level=1), Item(level=2)], 1=[Item(level=5)]}
Upvotes: 6