问题描述:

Lets say I have a set of a class Action like this: actions: Set[Action], and each Action class has a val consequences : Set[Consequence], where Consequence is a case class.

I wish to get a map from Consequence to Set[Action] to determine which actions cause a specific Consequence. Obviously since an Action can have multiple Consequences it can appear in multiple sets in the map.

I have been trying to get my head around this (I am new to Scala), wondering if I can do it with something like map() and groupBy(), but a bit lost. I don't wish to revert to imperative programming, especially if there is some Scala mapping function that can help.

What is the best way to achieve this?

网友答案:

Not exactly elegant because groupBy doesn't handle the case of operating already on a Tuple2, so you end up doing a lot of tupling and 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))

The first line "zips" each action with all of its consequences, yielding a Seq[(Conseq, Action)], grouping this by the first product element gives Map[Conseq, Seq[(Conseq, Action)]. So the last step needs to transform the map's values from Seq[(Conseq, Action)] to a Set[Action]. This can be done with mapValues. Without the explicit builder factory, it would produce a Seq[Action], so one would have to write .mapValues(_.map(_._2)).toSet. Passing in collection.breakOut in the second parameter list to map makes it possible to save one step and make map directly produce the Set collection type.


Another possibility is to use nested folds:

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

This is perhaps more readable. We start with an empty result map, traverse the actions, and in the inner fold traverse each action's consequences which get merged into the result map.

相关阅读:
Top