Scala - reduce / foldLeft
I have a nested map m that looks like:
m = Map("email" -> " a@b.com ", "background" -> Map("language" -> "english"))
I have an arr = Array("background","language") array
How to make foldLeft / reduce the array and find the string "english" from the map. I tried this:
arr.foldLeft(m) { (acc,x) => acc.get(x) }
But I get this error:
<console>:10: error: type mismatch; found : Option[java.lang.Object] required: scala.collection.immutable.Map[java.lang.String,java.lang.Object] arr.foldLeft(m) { (acc,x) => acc.get(x) } You should pay attention to types. Here you start with m : Map[String, Any] as your acc. You concatenate with string x and call get , which returns Option[Object] . To continue, you must check if there is a value, check if it is a Map value, casting (unchecked due to type erasure, therefore dangerous).
I believe the error is that the type of your structure, Map [String, Any], is something that you have is pretty bad.
Suppose you instead
sealed trait Tree case class Node(items: Map[String, Tree]) extends Tree case class Leaf(s: String) extends Tree You can add some helpers to simplify the Tree declaration.
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 a tree is as simple as your first version, but the type is much more accurate
m = Node("email" -> " a@b.com ", "background" -> Node("language" -> "english")) Then you can add methods, for example, to 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 prefer to do it with a crease
def get(path: String*) = path.foldLeft[Option[Tree]](Some(this)) { case (Some(Node(map)), p) => map.get(p) case _ => None } Folding as an abstraction over nested maps is really not supported. In addition, you approach this in a way that prevents the use of the type system. But, if you insist, then you need a recursive function:
def lookup(m: Map[String,Object], a: Array[String]): Option[String] = { if (a.length == 0) None else m.get(a(0)).flatMap(_ match { case mm: Map[_,_] => lookup(mm.asInstanceOf[Map[String,Object]],a.tail) case s: String if (a.length==1) => Some(s) case _ => None }) }