How to get value from nested map of type AnyRef

892 Views Asked by At

So I have a map of type

Map[String, AnyRef]

When I print this map through println, it gives the following output

Map(revision -> 
 Map(comment -> "string1", 
     contributor -> Map(id -> "int1", username -> "string2"), 
     format -> "string3", 
     id -> "int2", 
     minor -> None, 
     model -> "string4", 
     parentid -> "int3", 
     sha1 -> "string5", 
     text -> Map(_VALUE -> "VALUE-THAT-I-WANT-TO-GET", 
     space -> ""), 
     timestamp -> Timestamp, 
     title -> "string6"))

Now as you see in the map, I want to get the value against key _VALUE.

I tried getting it the way you get from a nested map explained in this answer, but it didn't work may be because it's of type AnyRef
What is the best way to get it in a simple string variable?

I am sorry if map is not readable enough, I will accept if you edit it in a better way. But it had be posted complete to clear sense of the problem.

3

There are 3 best solutions below

3
jwvh On BEST ANSWER

So let's say you have a coworker that produces code like that. Of course that person gets fired for being incompetent. Alas, you get stuck with the task of working with it until it can be rewritten into proper Scala.

Here's one thing you might do. It compiles with a warning but appears to produce the desired result.

def extract(m :collection.Map[String,AnyRef], key :String) :Option[String] = {
  if (m.isDefinedAt(key)) Some(m(key).toString)
  else {
    val res = m.values
               .collect{case ma:Map[String,AnyRef] => extract(ma,key)}
               .dropWhile(_.isEmpty)
    if (res.isEmpty) None else res.head
  }
}

extract(badMap, "_VALUE") //res0: Option[String] = Some(VALUE-THAT-I-WANT-TO-GET)
extract(badMap, "_XALUE") //res1: Option[String] = None
0
Frederic A. On

The verbose (but explicit) version could be:

yourMap.get("revision") collect {
  case Some(otherMap: Map[String, AnyRef]) => otherMap.get("text")
} collect {
  case Some(yetAnotherMap: Map[String, AnyRef]) => yetAnotherMap.get("_VALUE")
}
3
jgoday On

You can wrap your map (alias anymap) to define a getAs[T] method that returns an Option an so on ...

import scala.reflect.ClassTag

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

type AnyMap = Map[String, AnyRef]

implicit class AnyMapWrapper(m: AnyMap) {
    def getAs[T](key: String)(implicit ev: ClassTag[T]): Option[T] = m(key) match {
        case i: T => Some(i.asInstanceOf[T])
      case _ => None
  }
}

println {
    m.getAs[AnyMap]("a").flatMap(_.getAs[AnyMap]("b")).map(_("c"))
}

println {
  for {
    a <- m.getAs[AnyMap]("a")
    b <- a.getAs[AnyMap]("b")
  } yield b("c")
}