【问题标题】:Filtering a Scala Multimap and outputting as a list of Tuples过滤 Scala Multimap 并输出为元组列表
【发布时间】:2012-07-22 10:39:22
【问题描述】:

我有一张使用多地图特征的地图,就像这样
val multiMap = new HashMap[Foo, Set[Bar]] with MultiMap[Foo, Bar]
我想结合对特定值过滤此地图
multiMap.values.filter(bar => barCondition)
将匹配结果展平为以下形式的元组列表
val fooBarPairs: List[(Foo, Bar)]
这样做的惯用方式是什么?我希望 Scala 可以提供类似变形的东西来做到这一点而无需循环,但作为一个完整的新手,我不确定我的选择是什么。

【问题讨论】:

  • 如果 foo 映射到一组都满足过滤谓词的条,结果列表是否应该包含所有条的元组 (foo, bar)?如果您提供一些说明性示例,将会很有帮助。
  • 在我的问题中,“匹配结果”旨在说明我只对通过过滤条件的酒吧感兴趣,如果不清楚,请见谅。

标签: scala functional-programming tuples multimap


【解决方案1】:

这是一个例子:

import collection.mutable.{HashMap, MultiMap, Set}

val mm = new HashMap[String, Set[Int]] with MultiMap[String, Int]
mm.addBinding("One", 1).addBinding("One",11).addBinding("Two",22).
  addBinding("Two",222)
  // mm.type = Map(Two -> Set(22, 222), One -> Set(1, 11))

我认为获得所需内容的最简单方法是使用 for 表达式:

for {
  (str, xs) <- mm.toSeq
  x         <- xs
  if x > 10
} yield (str, x)      // = ArrayBuffer((Two,222), (Two,22), (One,11))

您需要.toSeq,否则输出类型将是Map,这意味着每个映射都被后续元素覆盖。如果您特别需要 List,请在此输出上使用 toList

【讨论】:

  • 我喜欢,序列理解很适合这个问题。
  • 如果您使用collection.breakOut,则不需要toSeq
  • @Debilski 很感兴趣。你可以在 for 表达式中使用 breakOut 吗?或者只是当你把它脱糖到像@mhs's answer之类的东西时。
  • (for {...} yield ())(breakOut) : List[(String, Int)] 应该确实有效。
【解决方案2】:

这是我认为你想做的一个例子:

scala> mm
res21: scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[Int]] with scala.collection.mutable.MultiMap[String,Int]
= Map(two -> Set(6, 4, 5), one -> Set(2, 1, 3))

scala> mm.toList.flatMap(pair =>
         pair._2.toList.flatMap(bar =>
           if (bar%2==0)
             Some((pair._1, bar))
           else
             None))

res22: List[(String, Int)] = List((two,6), (two,4), (one,2))

【讨论】:

  • 这很有帮助,我没有考虑过进行两级细分。我只使用匹配类型,所以我想我必须再次过滤 None 类型。不过,这似乎是一个很好的通用解决方案。
【解决方案3】:

这是另一个更简洁的解决方案:

import collection.mutable.{HashMap, MultiMap, Set}

val mm = new HashMap[String, Set[Int]] with MultiMap[String, Int]
val f = (i: Int) => i > 10

mm.addBinding("One", 1)
  .addBinding("One",11)
  .addBinding("Two",22)
  .addBinding("Two",222)
  /* Map(Two -> Set(22, 222), One -> Set(1, 11)) */

mm.map{case (k, vs) => vs.filter(f).map((k, _))}.flatten
  /* ArrayBuffer((Two,222), (Two,22), (One,11)) */

【讨论】:

  • 过滤然后重建地图似乎是解决这个问题的好方法,如果可以的话,我会给你积分。我确实需要将结果作为列表而不是 flatten 函数给出的迭代器。
猜你喜欢
  • 2011-05-27
  • 2016-11-15
  • 2012-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-09
  • 2013-05-03
相关资源
最近更新 更多