Reputation: 2277
I have string like this.
val input = "perm1|0,perm2|2,perm2|1"
Desired output type is
val output: Set<String, Set<Long>>
and desired output value is
{perm1 [], perm2 [1,2] }
Here I need empty set if value is 0
. I am using groupByTo
like this
val output = input.split(",")
.map { it.split("|") }
.groupByTo(
mutableMapOf(),
keySelector = { it[0] },
valueTransform = { it[1].toLong() }
)
However the output structure is like this
MutableMap<String, MutableList<Long>>
and output is
{perm1 [0], perm2 [1,2] }
I am looking for best way to get desired output without using imperative style like this.
val output = mutableMapOf<String, Set<Long>>()
input.split(",").forEach {
val t = it.split("|")
if (t[1].contentEquals("0")) {
output[t[0]] = mutableSetOf()
}
if (output.containsKey(t[0]) && !t[1].contentEquals("0")) {
output[t[0]] = output[t[0]]!! + t[1].toLong()
}
if (!output.containsKey(t[0]) && !t[1].contentEquals("0")) {
output[t[0]] = mutableSetOf()
output[t[0]] = output[t[0]]!! + t[1].toLong()
}
}
Upvotes: 2
Views: 681
Reputation: 23242
While the other answer(s) might be easier to grasp, they build immediate lists and maps in between, that are basically discarded right after the next operation. The following tries to omit that using splitToSequence
(Sequences) and groupingBy
(see Grouping bottom part):
val result: Map<String, Set<Long>> = input.splitToSequence(',')
.map { it.split('|', limit = 2) }
.groupingBy { it[0] }
.fold({ _, _ -> mutableSetOf<Long>() }) { _, accumulator, element ->
accumulator.also {
it.add(element[1].toLong()))
}
}
You can of course also filter out the addition of 0
in the set with a simple condition in the fold
-step:
// alternative fold skipping 0-values, but keeping keys
.fold({ _, _ -> mutableSetOf<Long>() }) { _, accumulator, element ->
accumulator.also {
val value = element[1].toLong()
if (value != 0L)
it.add(value)
}
}
Alternatively also aggregating might be ok, but then your result
-variable needs to change to Map<String, MutableSet<Long>>
:
val result: Map<String, MutableSet<Long>> = // ...
.aggregate { _, accumulator, element, first ->
(if (first) mutableSetOf<Long>() else accumulator!!).also {
val value = element[1].toLong()
if (value != 0L)
it.add(value)
}
}
Upvotes: 0
Reputation: 40008
You can simply use mapValues to convert values type from List<Long>
to Set<Long>
var res : Map<String, Set<Long>> = input.split(",")
.map { it.split("|") }
.groupBy( {it[0]}, {it[1].toLong()} )
.mapValues { it.value.toSet() }
And of you want to replace list of 0
with empty set you can do it using if-expression
var res : Map<String, Set<Long>> = input.split(",")
.map { it.split("|") }
.groupBy( {it[0]}, {it[1].toLong()} )
.mapValues { if(it.value == listOf<Long>(0)) setOf() else it.value.toSet() }
Upvotes: 5
Reputation: 15234
Note that you cannot have Set with key-value pair, result will be of type map. Below code gives sorted set in the values.
val result = "perm1|0,perm2|2,perm2|1".split(",")
.map {
val split = it.split("|")
split[0] to split[1].toLong()
}.groupBy({ it.first }, { it.second })
.mapValues { it.value.toSortedSet() }
Upvotes: 1