(Kotlin newbie here) I have a text file with rows that look like these:
1-1-1
1-1-2
1-1-3
2-1-1
2-1-2
etc.
I have to transform these data to a map where the key is the first 2 elements and the value is a list of the third elements that that match the key. For example, the above records will transform into this JSON:
1-1: [1, 2, 3]
2-1: [1, 2]
etc.
I'm unable to increment the list. Here's a simplified version, I get stuck on the "else":
fun main () {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, List<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
if (m[key] == null) m[key] = listOf(c[2].toInt())
else println("How do I append to the list?")
}
println(m)
}
Output:
{1-1=[1], 2-1=[1]}
But I want:
{1-1=[1, 2, 3], 2-1=[1, 2]}
Thank you (comments about idiomatic form are welcome!)
CodePudding user response:
If we continue to follow your strategy, what you need is for the value type to be a MutableList. Then you can add to the existing MutableList when there's already an existing list for that key:
fun main() {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, MutableList<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
if (m[key] == null) m[key] = mutableListOf(c[2].toInt())
else m[key]!!.add(c[2].toInt())
}
println(m)
}
This can be more natural using getOrPut()
. It returns the existing MutableList or creates one and puts it in the map if it's missing. Then we don't have to deal with if/else, and can simply add the new item to the list.
fun main() {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, MutableList<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
m.getOrPut(key, ::mutableListOf).add(c[2].toInt())
}
println(m)
}
But we can use the map
and groupBy
functions to create it more simply:
val m = l1.map { it.split("-") }
.groupBy(
{ "${it[0]}-${it[1]}" }, // keys
{ it[2].toInt() } // values
)
CodePudding user response:
You can also do it by first mapping your values to Pair
s and then group them as follows:
fun main(args: Array<String>) {
val input = listOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val result = input.map {
val values = it.split("-")
"${values[0]}-${values[1]}" to values[2]
}.groupBy ({ it.first }) { it.second }
println(result)
}
CodePudding user response:
You can achieve your desired output with a single call to groupBy
of the Kotlin standard library.
val input = listOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val result = input.groupBy(
{ it.substringBeforeLast("-") }, // extract key from item
{ it.substringAfterLast("-").toInt() } // extract value from item
)
The first lambda function extracts the key to group by of every list item. The second lambda function provides the value to use for each list item.