我有Map[A, Map[B, C]]
类型的地图。
如何将其反转为具有Map[B, Map[A, C]]
类型的地图?
答案 0 :(得分:6)
您可以通过多种方式定义此操作。我将介绍一些我认为最清楚的问题。对于第一个实现,我将从辅助方法开始:
def flattenNestedMap[A, B, C](nested: Map[A, Map[B, C]]): Map[(A, B), C] =
for {
(a, innerMap) <- nested
(b, c) <- innerMap
} yield (a, b) -> c
这会将嵌套地图展平为从对到值的地图。接下来,我们可以定义另一个帮助操作,几乎可以满足我们的需要。
def groupByBs[A, B, C](flattened: Map[(A, B), C]): Map[B, Map[(A, B), C]] =
flattened.groupBy(_._1._2)
现在我们只需要从内部地图中的键中删除多余的B
:
def invert[A, B, C](nested: Map[A, Map[B, C]]): Map[B, Map[A, C]] =
groupByBs(flattenNestedMap(nested)).mapValues(
_.map {
case ((a, _), c) => a -> c
}
)
(请注意,mapValues
是懒惰的,这意味着每次使用时都会重新计算结果。一般情况下,这不是问题,并且有简单的解决方法,但是它们很重要与问题无关。)
我们已经完成了:
scala> invert(Map(1 -> Map(2 -> 3), 10 -> Map(2 -> 4)))
res0: Map[Int,Map[Int,Int]] = Map(2 -> Map(1 -> 3, 10 -> 4))
您也可以跳过辅助方法,只需将invert
中的操作链接起来。我发现它们更清晰一些,但这只是一种风格问题。
或者你可以使用几个折叠:
def invert[A, B, C](nested: Map[A, Map[B, C]]): Map[B, Map[A, C]] =
nested.foldLeft(Map.empty[B, Map[A, C]]) {
case (acc, (a, innerMap)) =>
innerMap.foldLeft(acc) {
case (innerAcc, (b, c)) =>
innerAcc.updated(b, innerAcc.getOrElse(b, Map.empty).updated(a, c))
}
}
同样的事情:
scala> invert(Map(1 -> Map(2 -> 3), 10 -> Map(2 -> 4)))
res1: Map[Int,Map[Int,Int]] = Map(2 -> Map(1 -> 3, 10 -> 4))
foldLeft
版本具有更直接命令式版本的更多形状 - 我们(通过功能)迭代外部和内部地图的键值对并构建结果。在我的头脑中,我猜它也有点效率,但我对此并不确定,并且它不太重要,所以我&#39;建议选择你个人认为更清楚的那个。
答案 1 :(得分:-1)
您可以使用给定地图集合上的地图操作来执行此操作:
scala> Map("A" -> Map("B" -> "C"), "X" -> Map("Y" -> "Z"))
res1: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,String]] = Map(A -> Map(B -> C), X -> Map(Y -> Z))
scala> res1.map{ case (key, valueMap) => valueMap.map{ case (vmKey, vmValue) => (vmKey -> Map(key -> vmValue)) } }
res2: scala.collection.immutable.Iterable[scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,String]]] = List(Map(B -> Map(A -> C)), Map(Y -> Map(X -> Z)))