sachsure
sachsure

Reputation: 886

Invert Map<K, List<V>> to Map<V, K>

map = mapOf((2: [3,4,5]), (7: [22,33,44]))

need to convert this to

mapOf(3:2, 4:2, 5:2, 22:7, 33:7, 44:7)

tried using associate with forEach, not sure of the syntax

Upvotes: 1

Views: 170

Answers (2)

Adam Millerchip
Adam Millerchip

Reputation: 23091

Zymus' answer is correct, and is also what I would probably write.

However, if this is something that will be called often, you might want to extract it to a separate function that is more efficient.

fun <K, V> Map<K, Iterable<V>>.invert(): Map<V, K> {
    val newMap = mutableMapOf<V, K>()
    for ((key, iterable) in this) {
        for (value in iterable) {
            newMap[value] = key
        }
    }
    return newMap
}

Usage:

fun main() {
    val map = mapOf((2 to listOf(3, 4, 5)), (7 to listOf(22, 33, 44)))
    val inverted = map.invert()
    println(inverted)
}

Output:

{3=2, 4=2, 5=2, 22=7, 33=7, 44=7}

This is functionally equivalent to

map.flatMap { (key, values) -> values.map { it to key } }.toMap()

including the behaviour where if there are duplicate values in the original input, only the last one will be preserved as a new key. However, the flatMap version creates many temporary Lists (the number of original keys + 1) and many temporary Pairs (the number of original values), whereas this iterative version creates no extra objects.

Upvotes: 1

Zymus
Zymus

Reputation: 1698

There might be some nicer syntax, but this should work well enough.

fun main() {
    val map = mapOf(
        2 to listOf(3, 4, 5),
        7 to listOf(22, 33, 44)
    )
    val transformedMap = map.flatMap { entry ->
        entry.value.map { it to entry.key }
    }.toMap()
    println(transformedMap)
}

Prints {3=2, 4=2, 5=2, 22=7, 33=7, 44=7}

Note that the toMap function states

The returned map preserves the entry iteration order of the original collection. If any of two pairs would have the same key the last one gets added to the map.

So if you have the same value in two different lists, only the last one will be included in the map.

fun main() {
    val map = mapOf(
        2 to listOf(3, 4, 5),
        7 to listOf(22, 33, 44),
        8 to listOf(3)
    )
    val transformedMap = map.flatMap { entry ->
        entry.value.map { it to entry.key }
    }.toMap()
    println(transformedMap)
}

Prints {3=8, 4=2, 5=2, 22=7, 33=7, 44=7}

Upvotes: 4

Related Questions