" a@b.com ", "background" -> Map("language" -> "english")) ...">

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) } 
+4
source share
2 answers

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 } 
+12
source

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 }) } 
+4
source

All Articles