在Scala中反转嵌套Map

时间:2017-01-05 15:43:03

标签: scala dictionary

我有Map[A, Map[B, C]]类型的地图。

如何将其反转为具有Map[B, Map[A, C]]类型的地图?

2 个答案:

答案 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)))