将值转换为值映射

时间:2013-12-07 01:43:28

标签: scala map

假设我有一组Action这样的课程:actions: Set[Action],每个Action课程都有val consequences : Set[Consequence],其中Consequence是案例类。

我希望获得从ConsequenceSet[Action]的地图,以确定哪些操作会导致特定Consequence。显然,由于Action可以有多个Consequence,因此它可以显示在地图中的多个集合中。

我一直试图解决这个问题(我是Scala的新手),想知道我是否能用map()和groupBy()这样做,但有点迷失。我不希望恢复命令式编程,特别是如果有一些Scala映射函数可以提供帮助。

实现这一目标的最佳方法是什么?

1 个答案:

答案 0 :(得分:2)

不完全优雅,因为groupBy无法处理已在Tuple2上运行的情况,因此您最终会进行大量的tupling和untupling:

case class Conseq()
case class Action(conseqs: Set[Conseq])

def gimme(actions: Seq[Action]): Map[Conseq, Set[Action]] = 
  actions.flatMap(a => a.conseqs.map(_ -> a))
    .groupBy(_._1)
    .mapValues(_.map(_._2)(collection.breakOut))

第一行“拉链”每个动作及其所有后果,产生Seq[(Conseq, Action)],将第一个产品元素分组给Map[Conseq, Seq[(Conseq, Action)]。因此,最后一步需要将地图的值从Seq[(Conseq, Action)]转换为Set[Action]。这可以使用mapValues完成。如果没有显式构建器工厂,它将生成Seq[Action],因此必须编写.mapValues(_.map(_._2)).toSet。将第二个参数列表中的collection.breakOut传递给map,可以保存一个步骤,并使map直接生成Set集合类型。


另一种可能性是使用嵌套折叠:

def gimme2(actions: Seq[Action]) = (Map.empty[Conseq, Set[Action]] /: actions) {
  (m, a) => (m /: a.conseqs) {
    (m1, c) => m1.updated(c, m1.getOrElse(c, Set.empty) + a)
  }
}

这可能更具可读性。我们从空结果映射开始,遍历操作,并在内部折叠中遍历每个操作的后果,这些后果将合并到结果映射中。