Scala - 将子列表合并为包含在地图值内部的单个列表

Scala - Merge sub lists into a single list contained inside values of a map

我正在尝试以实用的方式解决以下问题。假设我有一个如下所示的数据结构:

final case class ActionConfiguration(
  configId: Int,
  actions:    List[Action],
  `type`:     String,
  time:       String,
  weekday:    Option[List[Int]]
)

我有一个具有以下签名的地图:Map[TargetLocation, List[ActionConfiguration]]

如您所见,我想在配置的时间和工作日在目标位置执行一些操作。它目前的工作方式是,如果我有一组要在同一时间和同一天执行的操作,我只会在操作列表中填充一个操作并创建许多可能具有相同配置时间和日期的 ActionConfiguration 对象。我的目标是利用 Actions 列表来填充我想在单个 ActionConfiguration 中执行的所有操作。

举个例子,鉴于我今天的情况:

"ActionConfiguration":[
            {
              "configId":1,
              "actions":[
                {
                  "cmd":"doAction1"
                }
              ],
              "type":"weekly",
              "time":"09:00",
              "weekday":[
                5
              ]
            },
            {
              "configId":2,
              "actions":[
                {
                  "cmd":"doAction2"
                }
              ],
              "type":"weekly",
              "time":"09:00",
              "weekday":[
                5
              ]
            },
            {
              "configId":3,
              "actions":[
                {
                  "cmd":"doAction3"
                }
              ],
              "type":"weekly",
              "time":"09:00",
              "weekday":[
                5
              ]
            },
            {
              "configId":4,
              "actions":[
                {
                  "cmd":"doAction4"
                }
              ],
              "type":"weekly",
              "time":"09:00",
              "weekday":[
                5
              ]
            },
            
            {
              "configId":5,
              "actions":[
                {
                  "cmd":"doAction5"
                }
              ],
              "type":"weekly",
              "time":"22:00",
              "weekday":[
                4
              ]
            }
          ]

我想实现这个:

"ActionConfiguration": [
            {
              "configId": 1,
              "actions": [
                {
                  "cmd": "doAction1"
                },
                {
                  "cmd": "doAction2"
                },
                {
                  "cmd": "doAction3"
                },
                {
                  "cmd": "doAction4"
                }
              ],
              "type": "weekly",
              "time": "09:00",
              "weekday": [
                5
              ]
            },
            
            {
              "configId": 2,
              "actions": [
                {
                  "cmd": "doAction5"
                }
              ],
              "type": "weekly",
              "time": "22:00",
              "weekday": [
                4
              ]
            }
          ]          

如您所见,我想将需要同时执行的操作合并到一个列表中。我来自 Java 背景,目前正在研究 Scala。我知道如何以 Java 风格解决这个问题,但我正在寻找一些我们如何以函数式风格解决这个问题的方法,因为我对学习函数式编程非常感兴趣。

假设有一个 ActionConfiguration 的构造函数按照下面的顺序接受成员,这可能有效(我只是在这里键入它,我没有编译或测试它):

  val mergedList = listofActionConfigurations
     .groupBy(a => (a.configId, a.time, a.type, a.weekday))
     .values
     .map(la => (la.head, la.map(_.actions).flatten))
     .map(t => new ActionConfiguration(t._1.configId, t._1.time, t._1.type, t._1.weekday, t._2))
     .toList
     

这里发生了什么?

我们通过一个键对 ActionConfigurations 列表进行分组,该键是通过对 ActionConfigurations 的所有字段进行元组化而创建的,除了操作列表。

然后我们扔掉键,只取值,这是一些 Set-like 包含 ActionConfigurations 列表的集合。

我们映射该集合,对于其中的每个 ActionConfigurations 列表,我们获取列表的第一个元素 (.head),然后我们将列表中的每个 ActionConfiguration 映射到 its 动作列表,然后我们展平结果列表列表。我们产生了头项和扁平化列表的元组。

然后我们将元组(head,动作列表)映射到一个新的 ActionConfiguration,其中包含 head 的所有成员,除了它的动作列表,并将扁平化的动作列表作为新的 ActionConfiguration 的动作列表。

最后,由于我们可能还有一个 Set-like 集合,我们明确地将其转换为一个列表。

解释比写要花更多的时间。 :)

请注意,Scala 库文档不保证合并操作列表中操作的顺序(但实现似乎是 order-preserving);如果您需要对其进行排序,请先展平然后再排序。

看来你只需要 groupMapReduce 创建一个 Map[TargetLocation, List[Action]] 如果你真的想要一个 Map[TargetLocation, List[ActionConfiguration]]

,你可以稍后 map
configurations.groupMapReduce(
  ac => (ac.configId, ac.time, ac.type, ac.weekday)
)(
  ac => ac.actions
)(
  (l1, l2) => l1 reverse_::: l2 // Similar to l1 ++ l2 but more efficient, although the order changes.
)