scala groupby 按地图列表中地图的值

scala groupby by the value of the map in a list of maps

所以我有一个 maps 这样的列表

val data = List(
           Map[String, String]("name" -> "Bob", "food" -> "pizza", "day" -> "monday"),
           Map[String, String]("name" -> "Ron", "food" -> "hotdog", "day" -> "tuesday"),
           Map[String, String]("name" -> "Tim", "food" -> "pizza", "day" -> "wednesday"),
           Map[String, String]("name" -> "Carl", "food" -> "hotdog", "day" -> "wednesday")
           )

我想根据 maps

的列表制作这样的地图
val result = Map("pizza" -> Map("name" -> ("Bob", "Tim"), "day" -> ("monday", "wednesday")),
                 "hotdog"-> Map("name" -> ("Ron", "Carl"), "day" -> ("tuesday", "wednesday")))

我怎样才能达到这个结果?谢谢

*ps 我是 Scala 的初学者

这是一个初步的解决方案,使用 fold 可能有更简单的方法,但我必须单独勾勒出来

 data.groupMap(a => a("food"))(_.filter(_._1 != "food"))
   .map{
      case (a,b) => 
         (a, b.flatten.groupMapReduce(_._1)(a => List(a._2))(_ ++ _))}
  1. 您根据 food
  2. 的值对内部地图进行分组

这给你:

Map(
  hotdog -> List(
               Map(name -> Ron, food -> hotdog, day -> tuesday), 
               Map(name -> Carl, food -> hotdog, day -> wednesday)), 
  pizza -> List(
               Map(name -> Bob, food -> pizza, day -> monday), 
               Map(name -> Tim, food -> pizza, day -> wednesday))
)
  1. 您从内部映射
  2. 中删除了密钥food
Map(
  hotdog -> List(
               Map(name -> Ron, day -> tuesday), 
               Map(name -> Carl, day -> wednesday)), 
  pizza -> List(
               Map(name -> Bob,  day -> monday), 
               Map(name -> Tim,  day -> wednesday))
)
  1. 您使用 groupMapReduce

    “合并”里面的地图

    a) 按内键分组(即 nameday

    b) 将每个值映射到一个单例列表

    c) 连接列表

编辑:这是一个使用 foldLeft 的单通道解决方案,但我不认为我更喜欢这个。所有的密钥访问都是不安全的,如果您输入的密钥丢失,将会崩溃。所以理想情况下你需要使用 .get() 来取回一个选项并做一堆模式匹配

data.foldLeft(Map[String, Map[String, List[String]]]())((b, a) => { 
  val foodVal = a("food")
  b.get(foodVal) match{
    case None => b + (foodVal -> 
        List("name" -> List(a("name")), "day" -> List(a("day"))).toMap) 
    case Some(v : Map[String, List[String]]) => 
      b + (foodVal -> 
        List("name" -> (v("name") :+ a("name")), "day" -> (v("day") :+ a("day"))).toMap)
  }
})