0
votes

I have a Map[String, String].

Edit: val m: Map[String, Any] = Map("a" -> Map("b" -> Map("c" -> "e", "d" -> "g")))

Is there a way to convert my Map[String,Any] to Map[String,String]

What is the best way to convert this nested Map into another Map with values like

Map("a.b.c" -> "e", "a.b.d" -> "g")

I found an earlier solution that is for Map[String,Int]. The link is given here: How to make a nested map as dot separated strings of key and value in scala

I tried to change the code (given in the comments of the above question):

def traverse(el: String, acc: List[String] = List.empty[String]): Map[String, String] = el match {
  case leaf: String => Map(acc.reverse.mkString(".") -> leaf)
  case m: Map[String, String] => m flatMap {
    case (k, v) => traverse(v, k :: acc)
  }
}

traverse(m)

However, i get the following error: Scrutinee is incompatible with pattern type, found: Map[String, String], required: String

I am new to Scala, so am unable to modify the code, please suggest what is causing this.

Update Solution:

def unravel(badmap :Map[String,_]
          ,key:List[String] = Nil) :Map[String,String] =
badmap.foldLeft(Map.empty[String,String]){
  case (m,(k,v)) if v.isInstanceOf[Map[_,_]] =>
    println("Case1" + m)
    m ++ unravel(v.asInstanceOf[Map[String,_]], k::key)
  case (m,(k,v)) =>
    println("Case2: " + m)
    val tempv = if (v!=null) v.toString  else null
    m + ((k::key).reverse.mkString(".") -> tempv )
}

I had a null value which caused it to fail. I updated the solution given. Thank you!

1
case m: Map[String, Any] is what you need to keep for the pattern match.Shankar Shastri
So, I would have to change my Map[String, String] to Map[String,Any]?Keshvi Srivastava
the example map prints as Map(a -> Map(b -> Map(c -> e, d -> g))). I need it to be changed to a.b.c -> e @jwvhKeshvi Srivastava
Hi, i have made the changeKeshvi Srivastava
Map[String, Any] smells like json, you may be better using a better tool for handling such data type.Luis Miguel Mejía Suárez

1 Answers

1
votes

You're in a bad place (type Any).

Sometimes we have to use unwise and unsafe tools (runtime type casting) to get out of a bad situation.

def unravel(badmap :Map[String,_]
           ,key:List[String] = Nil) :Map[String,String] =
  badmap.foldLeft(Map.empty[String,String]){
    case (m,(k,v)) if v.isInstanceOf[Map[_,_]] =>
      m ++ unravel(v.asInstanceOf[Map[String,_]], k::key)
    case (m,(k,v)) =>
      m + ((k::key).reverse.mkString(".") -> v.toString)
  }

testing:

val m: Map[String, _] =
  Map("a" -> Map("b" -> Map("c" -> "e", "d" -> "g")))

unravel(m)
//res0: Map[String,String] = Map(a.b.c -> e, a.b.d -> g)

Much better to avoid situations like this. Go back and fix the code that produced that Map.