在 Scala 中反转/转置一对多映射

发布于 2024-10-28 18:13:02 字数 379 浏览 2 评论 0原文

Map[A, Set[B]] 转换为 Map[B, Set[A]] 的最佳方法是什么?

例如,如何将 a 转换

Map(1 -> Set("a", "b"),
    2 -> Set("b", "c"),
    3 -> Set("c", "d"))

为 a

Map("a" -> Set(1),
    "b" -> Set(1, 2),
    "c" -> Set(2, 3),
    "d" -> Set(3))

(我仅在这里使用不可变集合。我真正的问题与字符串或整数无关。:)

What is the best way to turn a Map[A, Set[B]] into a Map[B, Set[A]]?

For example, how do I turn a

Map(1 -> Set("a", "b"),
    2 -> Set("b", "c"),
    3 -> Set("c", "d"))

into a

Map("a" -> Set(1),
    "b" -> Set(1, 2),
    "c" -> Set(2, 3),
    "d" -> Set(3))

(I'm using immutable collections only here. And my real problem has nothing to do with strings or integers. :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

顾忌 2024-11-04 18:13:02

在 aioobe 和 Moritz 的帮助下:

def reverse[A, B](m: Map[A, Set[B]]) =
  m.values.toSet.flatten.map(v => (v, m.keys.filter(m(_)(v)))).toMap

如果你显式调用 contains ,它会更具可读性:

def reverse[A, B](m: Map[A, Set[B]]) =
  m.values.toSet.flatten.map(v => (v, m.keys.filter(m(_).contains(v)))).toMap

with help from aioobe and Moritz:

def reverse[A, B](m: Map[A, Set[B]]) =
  m.values.toSet.flatten.map(v => (v, m.keys.filter(m(_)(v)))).toMap

It's a bit more readable if you explicitly call contains:

def reverse[A, B](m: Map[A, Set[B]]) =
  m.values.toSet.flatten.map(v => (v, m.keys.filter(m(_).contains(v)))).toMap
凉城已无爱 2024-11-04 18:13:02

到目前为止我想出的最好的是

val intToStrs = Map(1 -> Set("a", "b"),
                    2 -> Set("b", "c"),
                    3 -> Set("c", "d"))

def mappingFor(key: String) =
    intToStrs.keys.filter(intToStrs(_) contains key).toSet

val newKeys = intToStrs.values.flatten
val inverseMap = newKeys.map(newKey => (newKey -> mappingFor(newKey))).toMap

Best I've come up with so far is

val intToStrs = Map(1 -> Set("a", "b"),
                    2 -> Set("b", "c"),
                    3 -> Set("c", "d"))

def mappingFor(key: String) =
    intToStrs.keys.filter(intToStrs(_) contains key).toSet

val newKeys = intToStrs.values.flatten
val inverseMap = newKeys.map(newKey => (newKey -> mappingFor(newKey))).toMap
初心未许 2024-11-04 18:13:02

或者使用折叠的另一种:

  def reverse2[A,B](m:Map[A,Set[B]])=
      m.foldLeft(Map[B,Set[A]]()){case (r,(k,s)) =>
         s.foldLeft(r){case (r,e)=>
            r + (e -> (r.getOrElse(e, Set()) + k))
         }
      }

Or another one using folds:

  def reverse2[A,B](m:Map[A,Set[B]])=
      m.foldLeft(Map[B,Set[A]]()){case (r,(k,s)) =>
         s.foldLeft(r){case (r,e)=>
            r + (e -> (r.getOrElse(e, Set()) + k))
         }
      }
梦里梦着梦中梦 2024-11-04 18:13:02

这是一个单语句解决方案

 orginalMap
 .map{case (k, v)=>value.map{v2=>(v2,k)}}
 .flatten
 .groupBy{_._1}
 .transform {(k, v)=>v.unzip._2.toSet}

这一点相当整齐 (*) 生成构造反向映射所需的元组

Map(1 -> Set("a", "b"),
    2 -> Set("b", "c"),
    3 -> Set("c", "d"))
.map{case (k, v)=>v.map{v2=>(v2,k)}}.flatten

,将

 List((a,1), (b,1), (b,2), (c,2), (c,3), (d,3))

其直接转换为映射会覆盖与重复键对应的值,尽管

添加 .groupBy{_._1} 得到这个

 Map(c -> List((c,2), (c,3)),
     a -> List((a,1)),
     d -> List((d,3)), 
     b -> List((b,1), (b,2)))

更接近的。将这些列表变成后半对的集合。

  .transform {(k, v)=>v.unzip._2.toSet}

给出

  Map(c -> Set(2, 3), a -> Set(1), d -> Set(3), b -> Set(1, 2))

QED :)

(*) YMMV

Here's a one statement solution

 orginalMap
 .map{case (k, v)=>value.map{v2=>(v2,k)}}
 .flatten
 .groupBy{_._1}
 .transform {(k, v)=>v.unzip._2.toSet}

This bit rather neatly (*) produces the tuples needed to construct the reverse map

Map(1 -> Set("a", "b"),
    2 -> Set("b", "c"),
    3 -> Set("c", "d"))
.map{case (k, v)=>v.map{v2=>(v2,k)}}.flatten

produces

 List((a,1), (b,1), (b,2), (c,2), (c,3), (d,3))

Converting it directly to a map overwrites the values corresponding to duplicate keys though

Adding .groupBy{_._1} gets this

 Map(c -> List((c,2), (c,3)),
     a -> List((a,1)),
     d -> List((d,3)), 
     b -> List((b,1), (b,2)))

which is closer. To turn those lists into Sets of the second half of the pairs.

  .transform {(k, v)=>v.unzip._2.toSet}

gives

  Map(c -> Set(2, 3), a -> Set(1), d -> Set(3), b -> Set(1, 2))

QED :)

(*) YMMV

热风软妹 2024-11-04 18:13:02

一个简单但可能不是超级优雅的解决方案:

  def reverse[A,B](m:Map[A,Set[B]])={
      var r = Map[B,Set[A]]()
      m.keySet foreach { k=>
          m(k) foreach { e =>
            r = r + (e -> (r.getOrElse(e, Set()) + k))
          }
      }
      r
  }

A simple, but maybe not super-elegant solution:

  def reverse[A,B](m:Map[A,Set[B]])={
      var r = Map[B,Set[A]]()
      m.keySet foreach { k=>
          m(k) foreach { e =>
            r = r + (e -> (r.getOrElse(e, Set()) + k))
          }
      }
      r
  }
她说她爱他 2024-11-04 18:13:02

我能想到的最简单的方法是:

// unfold values to tuples (v,k)
// for all values v in the Set referenced by key k
def vk = for {
  (k,vs) <- m.iterator
  v <- vs.iterator
} yield (v -> k)

// fold iterator back into a map
(Map[String,Set[Int]]() /: vk) {
// alternative syntax: vk.foldLeft(Map[String,Set[Int]]()) {
  case (m,(k,v)) if m contains k =>
    // Map already contains a Set, so just add the value
    m updated (k, m(k) + v)
  case (m,(k,v)) =>
    // key not in the map - wrap value in a Set and return updated map
    m updated (k, Set(v))
}

The easiest way I can think of is:

// unfold values to tuples (v,k)
// for all values v in the Set referenced by key k
def vk = for {
  (k,vs) <- m.iterator
  v <- vs.iterator
} yield (v -> k)

// fold iterator back into a map
(Map[String,Set[Int]]() /: vk) {
// alternative syntax: vk.foldLeft(Map[String,Set[Int]]()) {
  case (m,(k,v)) if m contains k =>
    // Map already contains a Set, so just add the value
    m updated (k, m(k) + v)
  case (m,(k,v)) =>
    // key not in the map - wrap value in a Set and return updated map
    m updated (k, Set(v))
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文