Home > OS >  How to convert complex list of objects into the map in Scala
How to convert complex list of objects into the map in Scala

Time:05-20

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.

  • Related