0

How can I flatten deep nested Maps in Scala returning a new map with the relevant nested key in dot notation?

Background: I am trying to do so for uJson.Objs, but since they are just mutable.LinkedHashMap[String, Value] under-the-hoods, answers for standard library maps should help as well.

For example:

   val original = Obj("Simple" -> "a",
        "nested" ->
          Obj("c1" ->
            Obj("c2" -> "a")))
   
   flattenObj(original) shouldEqual Obj("simple" -> "a", "nested.c1.c2" -> "a")

I have tried the following (based on this answer), but it works only for the first level, and still don't deal with naming new keys in dot notation:

    def flattenObj(o: Obj, delimiter: String = "."): Obj = {
      o.obj.flatMap {
        case (key, map: Obj) => map.obj
        case (key, value) => Map(key -> value)
      }
    }

It results in:

Actual: {"Simple":"a","c1":{"c2":"a"}}

But, I expect:

Expected: {"simple":"a","nested.c1.c2":"a"}

Thank you all.

YFl
  • 845
  • 7
  • 22
  • 2
    for one, your solution should be recursive. Something along the lines of ```def flattenObj(o: Obj, delimiter: String = ".", keyAcc: String=""): Obj = { o.obj.flatMap { case (key, map: Obj) => flattenObj(map.obj, keyAcc = keyAcc + "." + key) case (key, value) => Map(keyAcc + "." + key -> value) } }``` – Raf Dec 21 '21 at 13:23
  • Thanks, @Raf. Please add implemented based on your guidance and worked fine. I will post and answer with the final implementation to help others too. – YFl Dec 21 '21 at 16:51

1 Answers1

0

Final implementation based on the @Raf's guidance in the comments:

  def flattenObj(o: Obj, keyAcc: String = "", delimiter: String = "."): Obj = {
    o.obj.flatMap(kv => {
      val key: String = if (keyAcc.isEmpty) kv._1 else keyAcc + delimiter + kv._1

      kv._2 match {
        case map: Obj => flattenObj(map.obj, key, delimiter).obj
        case value => Map(key -> value)

      }
    })
  }

Thank you Raf.

YFl
  • 845
  • 7
  • 22