简体   繁体   English

反转Scala中的嵌套地图

[英]Inverse a nested Map in Scala

I have a Map of type Map[A, Map[B, C]] . 我有一个Map类型为Map[A, Map[B, C]]

How can I inverse it to have a Map of type Map[B, Map[A, C]] ? 如何使它具有Map[B, Map[A, C]]类型的Map[B, Map[A, C]]呢?

There are lots of ways you could define this operation. 您可以通过多种方式定义此操作。 I'll walk through a couple of the ones that I find the clearest. 我将介绍一些我认为最清楚的方法。 For the first implementation I'll start with a helper method: 对于第一个实现,我将从助手方法开始:

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

This flattens the nested map to a map from pairs to values. 这会将嵌套的地图展平为从对到值的地图。 Next we can define another helper operation that gets us almost what we need. 接下来,我们可以定义另一个帮助程序操作,使我们几乎可以得到所需的东西。

def groupByBs[A, B, C](flattened: Map[(A, B), C]): Map[B, Map[(A, B), C]] =
  flattened.groupBy(_._1._2)

Now we just need to remove the redundant B from the keys in the inner map: 现在我们只需要从内部映射中的键中删除冗余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
    }
  )

(Note that mapValues is lazy, which means that the result will be recomputed every time you use it. In general this isn't a problem, and there are easy workarounds, but they're not really relevant to the question.) (请注意, mapValues是惰性的,这意味着每次使用它时都会重新计算结果。通常这不是问题,并且有很容易的解决方法,但它们与问题并没有真正的关系。)

And we're done: 至此,我们完成了:

scala> invert(Map(1 -> Map(2 -> 3), 10 -> Map(2 -> 4)))
res0: Map[Int,Map[Int,Int]] = Map(2 -> Map(1 -> 3, 10 -> 4))

You could also skip the helper methods and just chain the operations in invert . 您也可以跳过辅助方法,而只是将操作invert I find breaking them up a little clearer, but that's a matter of style. 我发现将它们分解更清晰一些,但这是样式问题。

Alternatively you could use a couple of folds: 另外,您可以使用几折:

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))
      }
  }

Which does the same thing: 哪个做同样的事情:

scala> invert(Map(1 -> Map(2 -> 3), 10 -> Map(2 -> 4)))
res1: Map[Int,Map[Int,Int]] = Map(2 -> Map(1 -> 3, 10 -> 4))

The foldLeft version has more of the shape of the straightforward imperative version—we're (functionally) iterating through the key-value pairs of the outer and inner maps and building up the result. foldLeft版本具有直接命令式版本的更多形状-我们在功能上迭代外部和内部映射的键值对并构建结果。 Off the top of my head I'd guess it's also a little more efficient, but I'm not sure about that, and it's unlikely to matter much, so I'd suggest choosing the one you personally find clearer. 我想我的头顶上还有一点点效率,但是我不确定,这不太重要,所以我建议选择一个您自己觉得更清晰的型号。

You can simply do it using map operation on given Map collection : 您可以简单地使用给定Map集合上的map操作来做到这一点:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM