在Scala中转换地图的优雅方法

现在学习Scala并需要将一个Map转换为一些倒转的值 – >键查找。 我正在寻找一个简单的方法来做到这一点,但只想到:

(Map() ++ origMap.map(kvp=>(kvp._2->kvp._1))) 

任何人有一个更优雅的方法?

假设值是唯一的,这是有效的:

 (Map() ++ origMap.map(_.swap)) 

然而,在Scala 2.8上,它更容易:

 origMap.map(_.swap) 

能够做到这一点是斯卡拉2.8有一个新的collections库的原因之一。

在math上,映射可能是不可行的,例如从Map[A,B] ,你不能得到Map[B,A] ,而是你得到Map[B,Set[A]] ,因为可能有不同的与相同值相关联的键。 所以,如果你有兴趣知道所有的关键,这里是代码:

 scala> val m = Map(1 -> "a", 2 -> "b", 4 -> "b") scala> m.groupBy(_._2).mapValues(_.keys) res0: Map[String,Iterable[Int]] = Map(b -> Set(2, 4), a -> Set(1)) 

您可以避免._1的东西,而在几个方面迭代。

这是一种方法。 这使用了一个局部函数,它涵盖了唯一对地图很重要的情况:

 Map() ++ (origMap map {case (k,v) => (v,k)}) 

这是另一种方式:

 import Function.tupled Map() ++ (origMap map tupled {(k,v) => (v,k)}) 

映射迭代使用两个元素元组来调用函数,而匿名函数需要两个参数。 Function.tupled进行翻译。

我来到这里寻找一种将地图[A,Seq [B]]映射到地图[B,Seq [A]]的方法,其中新地图中的每个B都与旧地图中的每个A相关联B被包含在A的相关序列中。

例如,
Map(1 -> Seq("a", "b"), 2-> Seq("b", "c"))
会倒转到
Map("a" -> Seq(1), "b" -> Seq(1, 2), "c" -> Seq(2))

这是我的解决scheme:

 val newMap = oldMap.foldLeft(Map[B, Seq[A]]().withDefaultValue(Seq())) { case (m, (a, bs)) => bs.foldLeft(m)((map, b) => map.updated(b, m(b) :+ a)) } 

其中oldMap的types为Map[A, Seq[B]] ,newMap的types为Map[B, Seq[A]]

嵌套的foldLefts让我有点畏缩,但这是我能find的最直接的方式来完成这种反演。 任何人有更清洁的解决scheme

在斯卡拉REPL:

 scala> val m = Map(1 -> "one", 2 -> "two") m: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one, 2 -> two) scala> val reversedM = m map { case (k, v) => (v, k) } reversedM: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 1, two -> 2) 

请注意,重复的值将被最后一次添加到地图的内容覆盖:

 scala> val m = Map(1 -> "one", 2 -> "two", 3 -> "one") m: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one, 2 -> two, 3 -> one) scala> val reversedM = m map { case (k, v) => (v, k) } reversedM: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 3, two -> 2) 

你可以使用以下方法来反转地图

 val i = origMap.map({case(k, v) => v -> k}) 

这种方法的问题是,如果你的值,现在已经成为你的地图中的哈希键,是不唯一的,你会丢弃重复的值。 为了显示:

 scala> val m = Map("a" -> 1, "b" -> 2, "c" -> 3, "d" -> 1) m: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2, c -> 3, d -> 1) // Notice that 1 -> a is not in our inverted map scala> val i = m.map({ case(k , v) => v -> k}) i: scala.collection.immutable.Map[Int,String] = Map(1 -> d, 2 -> b, 3 -> c) 

为了避免这种情况,您可以先将映射转换为元组列表,然后进行反转,这样就不会丢失任何重复的值:

 scala> val i = m.toList.map({ case(k , v) => v -> k}) i: List[(Int, String)] = List((1,a), (2,b), (3,c), (1,d)) 
  1. 对于这个操作来说,反转是比反转更好的名称(如“math函数的反转”)

  2. 我经常不仅在地图上而且在其他(包括Seq)集合上进行逆向变换。 我觉得最好不要将我的逆操作的定义限制在一对一的映射中。 这里是我使用地图的定义(请build议对我的实现进行改进)。

     def invertMap[A,B]( m: Map[A,B] ) : Map[B,List[A]] = { val k = ( ( m values ) toList ) distinct val v = k map { e => ( ( m keys ) toList ) filter { x => m(x) == e } } ( k zip v ) toMap } 

如果它是一对一的映射,那么最终得到的单例列表可以进行简单的testing,并转换为Map [B,A]而不是Map [B,List [A]]。