Home > Net >  Kotlin: mutable map of mutable list won't update the list
Kotlin: mutable map of mutable list won't update the list

Time:12-05

(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 Pairs 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.

  • Related