I'm looking for transforming my case List of objects into the Map. This
input class looks like this
case class MyData(
source: String,
dataMap: Map[String, String]
)
Resultant class post-transformation
case class LiverampMappingV2(
dataMapping: Map[String, Map[String, Seq[String]]]
)
For example, following input data -->
val data = List(
new myData("abc.com", Map("key1" -> "value1", "key2" -> "value2")),
new myData("abc.com", Map("key1" -> "value11", "key3" -> "value3")),
new myData("pqr.com", Map("key1" -> "value111", "key3" -> "value33"))
)
will be assembled as follows
Map(
key1 -> Map("abc.com" -> List("value1", "value11"), "pqr.com" -> List("value111")),
key2 -> Map("abc.com" -> List("value2")),
key3 -> Map("abc.com" -> List("value13"), "pqr.com" -> List("value33"))
)
I tried a couple of options using combination of groupBy
and groupMap
followed by flatten
but being newbie to scala and functional programming, I can't reach to the final solution.
Any help or guidance is appreciated.
CodePudding user response:
I also can recommend another approach if you do not have cats in scope :D Although that's pretty verbose and a little complicated. And that's because you need to fold on each iteration over your first original "data". But I'll try to explain:
type MP = Map[String, List[String]]
// just for shortening the code, you can put a better name on it
data.foldLeft[Map[String, MP]](Map.empty)(
(agg, newData) => {
val source = newData.source
newData.dataMap.foldLeft(agg) {
case (updatingAggregator, (key, value)) =>
updatingAggregator.updatedWith(key) {
case Some(existingMap) =>
Some(existingMap.updatedWith(source) {
case Some(existingList) => Some(value :: existingList)
case None => Some(value :: Nil)
})
case None =>
Some(Map(source -> (value :: Nil)))
}
}
}
)
Basically what we're doing here is folding over the data
, with a zero value of empty Map of String to MP
(which is the resulting type we want from the algorithm, and from folding). On each iteration you have the source (val source = ...
). And you also have a dataMap, which you need to fold again (:D), and update the previously created agg
value inside it, using the given source, key and value. Now in the updatedWith method, you basically look for the source
inside the updatingAggregator, and if it already has it, you just prepend the value, and if not, you just make it.
Let me know if the description was not clear enough.
CodePudding user response:
If you have cats in scope, this is very simple since you can take advantage of the Monoid
of Map
import cats.syntax.all._
def groupByNested(data: List[MyData]): Map[String, Map[String, List[String]]] =
data.foldMap {
case MyData(source, dataMap) =>
dataMap.map {
case (key, value) =>
key -> Map(source -> (value :: Nil))
}
}
I am not totally happy with the function name.
You can see it running here.