您可以通过多种方式定义此操作.我会介绍一些我发现最清楚的东西.对于第一个实现,我将从一个辅助方法开始:
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
Run Code Online (Sandbox Code Playgroud)
这会将嵌套地图展平为从对到值的地图.接下来我们可以定义另一个帮助操作,它几乎可以满足我们的需要.
def groupByBs[A, B, C](flattened: Map[(A, B), C]): Map[B, Map[(A, B), C]] =
flattened.groupBy(_._1._2)
Run Code Online (Sandbox Code Playgroud)
现在我们只需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
}
)
Run Code Online (Sandbox Code Playgroud)
(注意,这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))
Run Code Online (Sandbox Code Playgroud)
您也可以跳过辅助方法并将操作链接起来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))
}
}
Run Code Online (Sandbox Code Playgroud)
哪个做同样的事情:
scala> invert(Map(1 -> Map(2 -> 3), 10 -> Map(2 -> 4)))
res1: Map[Int,Map[Int,Int]] = Map(2 -> Map(1 -> 3, 10 -> 4))
Run Code Online (Sandbox Code Playgroud)
该foldLeft版本具有更直接命令式版本的更多形状 - 我们(功能上)迭代外部和内部地图的键值对并构建结果.在我的脑海中,我猜它也有点效率,但我不确定,并且不太重要,所以我建议选择你个人觉得更清楚的那个.