如何在 Kotlin 中反转地图?
How to reverse a Map in Kotlin?
我正在尝试在 Kotlin 中反转地图。到目前为止,我想出了:
mapOf("foo" to 42)
.toList()
.map { (k, v) -> v to k }
.toMap()
有没有更好的方法不使用中间人(中间人)?
由于 Map
由 Entry
组成,而不是 Iterable
,您可以使用 Map#entries instead. It will be mapped to Map#entrySet 创建 Set<Entry>
的后备视图,因为示例:
val reversed = map.entries.associateBy({ it.value }) { it.key }
或使用Iterable#associate,这将创建额外的Pair
。
val reversed = map.entries.associate{(k,v)-> v to k}
或 使用 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.
这是一个简单的扩展函数,它可以反转映射 - 不会生成不需要的垃圾(如对、中间数据结构和不必要的闭包)
fun <K, V> Map<K, V>.reversed() = HashMap<V, K>().also { newMap ->
entries.forEach { newMap.put(it.value, it.key) }
}
注意apply
是内联的,entries.forEach
也是内联的(和Map::forEach
不一样)
我仍在学习 Kotlin 的来龙去脉,但我有相同的要求,从 Kotlin 1.2 开始,您似乎可以迭代 Map 和 map() 直接像这样:
@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())
}
如果您的地图不是 1-1 映射并且您希望反转为值列表:
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>>
}
如果您需要将 m: Map<K, List<V>>
之类的多重映射反转为 Map<V, List<K>>
,您可以执行
m
.flatMap { it.value.map { oneValue -> oneValue to it.key } }
.groupBy({ it.first }, { it.second })
.toMap()
依序,
mapOf('a' to listOf('b', 'c'), 'd' to listOf('b'))
被平面映射到像 这样的序列
listOf('b' to 'a', 'c' to 'a', 'b' to 'd')
分组到
listOf('b' to listOf('a', 'd'), 'c' to listOf('a'))
然后转换为地图。
这可能会创建中间对象。
这是我对 1:1 地图的看法
private fun <K, V> Map<K, V>.reverseOneToOneMap(): Map<V, K> {
val result = this.entries.associateBy({ it.value }) { it.key }
if (result.size != this.size) {
throw RuntimeException("Map must be 1:1")
}
return result
}
我正在尝试在 Kotlin 中反转地图。到目前为止,我想出了:
mapOf("foo" to 42)
.toList()
.map { (k, v) -> v to k }
.toMap()
有没有更好的方法不使用中间人(中间人)?
由于 Map
由 Entry
组成,而不是 Iterable
,您可以使用 Map#entries instead. It will be mapped to Map#entrySet 创建 Set<Entry>
的后备视图,因为示例:
val reversed = map.entries.associateBy({ it.value }) { it.key }
或使用Iterable#associate,这将创建额外的Pair
。
val reversed = map.entries.associate{(k,v)-> v to k}
或 使用 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.
这是一个简单的扩展函数,它可以反转映射 - 不会生成不需要的垃圾(如对、中间数据结构和不必要的闭包)
fun <K, V> Map<K, V>.reversed() = HashMap<V, K>().also { newMap ->
entries.forEach { newMap.put(it.value, it.key) }
}
注意apply
是内联的,entries.forEach
也是内联的(和Map::forEach
不一样)
我仍在学习 Kotlin 的来龙去脉,但我有相同的要求,从 Kotlin 1.2 开始,您似乎可以迭代 Map 和 map() 直接像这样:
@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())
}
如果您的地图不是 1-1 映射并且您希望反转为值列表:
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>>
}
如果您需要将 m: Map<K, List<V>>
之类的多重映射反转为 Map<V, List<K>>
,您可以执行
m
.flatMap { it.value.map { oneValue -> oneValue to it.key } }
.groupBy({ it.first }, { it.second })
.toMap()
依序,
mapOf('a' to listOf('b', 'c'), 'd' to listOf('b'))
被平面映射到像 这样的序列
listOf('b' to 'a', 'c' to 'a', 'b' to 'd')
分组到listOf('b' to listOf('a', 'd'), 'c' to listOf('a'))
然后转换为地图。
这可能会创建中间对象。
这是我对 1:1 地图的看法
private fun <K, V> Map<K, V>.reverseOneToOneMap(): Map<V, K> {
val result = this.entries.associateBy({ it.value }) { it.key }
if (result.size != this.size) {
throw RuntimeException("Map must be 1:1")
}
return result
}