Home > Mobile >  Count occurence of key values from several maps grouped by a key in scala 2.11.x
Count occurence of key values from several maps grouped by a key in scala 2.11.x

Time:12-19

Imagine the following list of maps (which could be potentially longer):

List(
Map[String,String]("wind"->"high", "rain"->"heavy", "class"->"very late"),
Map[String,String]("wind"->"none", "rain"->"slight", "class"->"on time"),
Map[String,String]("wind"->"high", "rain"->"none", "class"->"very late"),
...
)

How can I get to the following form:

Map("very late" -> Set(("wind",Map("high" -> 2)), ("rain",Map("heavy" -> 1, "none" -> 1))),
"on time" -> Set(("wind",Map("none" -> 1)), ("rain",Map("slight" -> 1))))

CodePudding user response:

Here are two versions.

First using Set, which looks like what you want,

val grouped2 = maps.foldLeft(Map.empty[String, Set[(String, Map[String, Int])]]) {
  case (acc, map) =>
    map.get("class").fold(acc) { key =>
      val keyOpt = acc.get(key)
      if (keyOpt.isDefined) {
        val updatedSet = (map - "class").foldLeft(Set.empty[(String, Map[String, Int])]) {
          case (setAcc, (k1, v1)) =>
            keyOpt.flatMap(_.find(_._1 == k1)).map { tup =>
              setAcc   ((k1, tup._2.get(v1).fold(tup._2    Map(v1 -> 1))(v => tup._2   ((v1, v   1)))))
            }.getOrElse(setAcc   (k1 -> Map(v1 -> 1)))
        }
        acc.updated(key, updatedSet)
      } else {
        acc   (key -> (map - "class").map(tup => (tup._1, Map(tup._2 -> 1))).toSet)
      }
    }
}

and then using a Map,

val grouped1 = maps.foldLeft(Map.empty[String, Map[String, Map[String, Int]]]) {
  case (acc, map) =>
    map.get("class").fold(acc) { key =>
      val keyOpt = acc.get(key)
      if (keyOpt.isDefined) {
        val updatedMap = (map - "class").foldLeft(Map.empty[String, Map[String, Int]]) {
          case (mapAcc, (k1, v1)) =>
            keyOpt.flatMap(_.get(k1)).map{ statMap =>
              mapAcc   ((k1, statMap.get(v1).fold(statMap    Map(v1 -> 1))(v => statMap   ((v1, v   1)))))
            }.getOrElse(mapAcc   (k1 -> Map(v1 -> 1)))
        }
        acc.updated(key, updatedMap)
      } else {
        acc   (key -> (map - "class").map(tup => (tup._1, Map(tup._2 -> 1))))
      }
    }
}

I was playing with Map version and changed it to Set. In a few days, I don't think I will understand everything above just by looking at it. So, I have tried to make it as understandable as possible for me. Adapt this to your own solution or wait for others.

CodePudding user response:

This will get you what you want.

val maps = List(...)

maps.groupBy(_.getOrElse("class","no-class"))
  .mapValues(_.flatMap(_ - "class").groupBy(_._1)
    .mapValues(_.map(_._2).groupBy(identity)
      .mapValues(_.length)
    ).toSet
  )

The problem is, what you want isn't a good place to be.

The result type is Map[String,Set[(String,Map[String,Int])]] which is a terrible hodgepodge of collection types. Why Set? What purpose does that serve? How is this useful? How do you retrieve meaningful data from it?

This looks like an XY problem.

  • Related