web-dev-qa-db-fra.com

Comment inverser une carte dans Kotlin?

J'essaie d'inverser une carte dans Kotlin. Jusqu'à présent, j'ai trouvé:

mapOf("foo" to 42)
  .toList()
  .map { (k, v) -> v to k }
  .toMap()

Existe-t-il une meilleure façon de procéder sans recourir à un intermédiaire (liste intermédiaire)?

17
Grzegorz Piwowarek

Étant donné que le Map se compose de Entry et qu'il ne s'agit pas de Iterable, vous pouvez utiliser Map # entries à la place. Il sera mappé sur Map # entrySet pour créer une vue sauvegardée de Set<Entry>, Par exemple:

val reversed = map.entries.associateBy({ it.value }) { it.key }

[~ # ~] ou [~ # ~] utilisez Iterable # associé , ce qui créera Pairs supplémentaires .

val reversed = map.entries.associate{(k,v)-> v to k}

[~ # ~] ou [~ # ~] en utilisant Map # forEach :

val reversed = mutableMapOf<Int, String>().also {
    //     v-- use `forEach` here     
    map.forEach { (k, v) -> it.put(v, k) } 
}.toMap()
// ^--- you can add `toMap()` to create an immutable Map.
37
holi-java

Voici une fonction d'extension simple qui inverse une carte - sans générer de déchets inutiles (comme des paires, des structures de données intermédiaires et des fermetures inutiles)

fun <K, V> Map<K, V>.reversed() = HashMap<V, K>().also { newMap ->
    entries.forEach { newMap.put(it.value, it.key) }
}

notez que apply est en ligne et entries.forEach est également en ligne (ce qui n'est pas la même chose pour Map::forEach)

3
bennyl

Dans le cas où votre carte n'est pas une correspondance 1-1 et que vous souhaitez que l'inversion soit une liste de valeurs:

mapOf(1 to "AAA", 2 to "BBB", 3 to "BBB").toList()
        .groupBy { pair -> pair.second } // Pair<Int, String>
        .mapValues { entry -> 
          entry.value.map { it.first } // Entry<String, List<Pair<Int, String>>
        }

1
Yair Galler

J'apprends toujours les tenants et les aboutissants de Kotlin, mais j'avais la même exigence et comme dans Kotlin 1.2, il semble que vous pouvez itérer sur une carte et donc map () directement comme ceci:

@Test
fun testThatReverseIsInverseOfMap() {
    val intMap = mapOf(1 to "one", 2 to "two", 3 to "three")
    val revMap = intMap.map{(k,v) -> v to k}.toMap()
    assertTrue(intMap.keys.toTypedArray() contentEquals revMap.values.toTypedArray())
    assertTrue(intMap.values.toTypedArray() contentEquals revMap.keys.toTypedArray())
}
0
Eoin

Si vous devez inverser un multimap comme m: Map<K, List<V>> En Map<V, List<K>>, Vous pouvez le faire

m
  .flatMap { it.value.map { oneValue -> oneValue to it.key } }
  .groupBy({ it.first }, { it.second })
  .toMap()

En séquence,

  • mapOf('a' to listOf('b', 'c'), 'd' to listOf('b')) est mappé à plat sur une séquence comme
  • listOf('b' to 'a', 'c' to 'a', 'b' to 'd') qui est regroupée en
  • listOf('b' to listOf('a', 'd'), 'c' to listOf('a')) qui est ensuite convertie en carte.

Cela crée probablement des objets intermédiaires.

0
Mike Samuel