Reputation: 9621
I have a case class like:
case class ReportData(
building: Option[String] = None,
serial: Option[String] = None,
`type`: Option[String] = None,
model: Option[String] = None,
machine: Option[String] = None)
and other fields too.
I have a list of these objects, for instance:
val reports = List(report1, report2, report3, report11, report12)
The request is to do some specific complex operations with specific fields of these class, for instance group these list only by the fields received as a parameter: groupFields="building,serial"
This is an example of a fine group by:
val reportsGroupBoth = reports.groupBy(p => (p.building.getOrElse(""), p.serial.getOrElse("")))
butI want to make it conditional depending of the fields received so the only way I am thinking is to make group by separated for each field (I will add if condition
later now I just want to receive the same result as the above group by
if I make them separated, result being of type: Map[(String, String),List[agile.ReportData]]
so I tried:
// group by building (if building is defined in groupFields list)
val reportsGroup1 = reports.groupBy(p => p.building)
which is fine but this one is wrong:
// group by building and serial (if building and serial are in groupFields list)
val reportsGroup2 = reportsGroup1 map {
case (key, value) => (key, value.groupBy(v => v.serial).keys) -> value.groupBy(v => v.serial).values
}
So the question is how to modify reportsGroup2
that in the end it will have the same result as reportsGroupBoth
of tpye Map[(String, String),List[agile.ReportData]]
Upvotes: 1
Views: 5685
Reputation: 843
If you want simply to group by two nonempty fields
reports.groupBy(r => (r.building, r.serial)).flatMap({
case ((Some(b),Some(s)),v) => Some((b,s) -> v)
case _ => None
})
If you want to group after group, like you suggested:
val reportsGroup2 = reportsGroup1.map({
case (key, value) => value.groupBy(v => (key, v.serial))
}).flatten.flatMap({
case ((Some(b),Some(s)),v) => Some((b,s) -> v)
case _ => None
}).toMap
You will get the same
Maybe you want to setup some grouping policy, like:
val buildingSerialPolicy =
Seq[ReportGroup => Option[String](_.building, _.serial)
And then just call needed policy:
val removeEmptyKeys = ...
report.groupBy(r =>
buildingSerialPolicy.map(_.apply(r))).flatMap(removeEmptyKeys)
Upvotes: 1
Reputation: 13667
This will work:
val reportsGroup1 = reports.groupBy(p => p.building.getOrElse(""))
val reportsGroup2 = reportsGroup1 flatMap {
case (key, value) => value.groupBy(v => v.serial.getOrElse("")).map {
case (key2, value) => ((key, key2), value)
}
}
We take each of the groups of the first groupBy
and do a groupBy
on it, then add the first key back in. Each of these separate groups of groups are combined and returned as one map.
Upvotes: 2