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)?
É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 Pair
s 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.
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
)
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>>
}
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())
}
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 commelistOf('b' to 'a', 'c' to 'a', 'b' to 'd')
qui est regroupée enlistOf('b' to listOf('a', 'd'), 'c' to listOf('a'))
qui est ensuite convertie en carte.Cela crée probablement des objets intermédiaires.