You should pay attention to types. Here, you start with m : Map[String, Any]
as your acc. You combine with a string x
and calls get
, which returns an Option[Object]
. To continue, you must check that there is a value, check whether this value is a Map
, cast (unchecked because of type erasure, hence dangerous).
I believe the fault is in the that the type of your structure, Map[String, Any] represents what you have rather poorly.
Suppose you do instead
sealed trait Tree
case class Node(items: Map[String, Tree]) extends Tree
case class Leaf(s: String) extends Tree
You may add some helpers to make declaring a Tree easy
object Tree {
implicit def fromString(s: String) = Leaf(s)
implicit def fromNamedString(nameAndValue: (String, String))
= (nameAndValue._1, Leaf(nameAndValue._2))
}
object Node {
def apply(items: (String, Tree)*) : Node = Node(Map(items: _*))
}
Then declaring the tree is just as easy as your first version, but the type is much more precise
m = Node("email" -> "[email protected]", "background" -> Node("language" -> "english"))
You can then add methods, for instance in trait Tree
def get(path: String*) : Option[Tree] = {
if (path.isEmpty) Some(this)
else this match {
case Leaf(_) => None
case Node(map) => map.get(path.head).flatMap(_.get(path.tail: _*))
}
}
def getLeaf(path: String*): Option[String]
= get(path: _*).collect{case Leaf(s) =>s}
Or if you would rather do it with a fold
def get(path: String*) = path.foldLeft[Option[Tree]](Some(this)) {
case (Some(Node(map)), p) => map.get(p)
case _ => None
}
m("background").asInstanceOf[Map[String,String]]("language")
.m("background")
is ajava.lang.Object
, you need an extra cast/match somewhere. However this data structure seems a bit odd and you should consider something more object oriented. – Tomasz Nurkiewicz