Скала - уменьшить / сложить
У меня есть вложенная карта m
что похоже на:
m = Map("email" -> "a@b.com", "background" -> Map("language" -> "english"))
У меня есть массив arr = Array("background","language")
Как мне сложить / уменьшить массив и найти строку "английский" с карты. Я попробовал это:
arr.foldLeft(m) { (acc,x) => acc.get(x) }
Но я получаю эту ошибку:
<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) }
2 ответа
Вы должны обратить внимание на типы. Здесь вы начинаете с m : Map[String, Any]
как ваш акк. Вы объединяете со строкой x
и звонки get
, который возвращает Option[Object]
, Для продолжения необходимо проверить наличие значения, проверить, является ли это значение Map
, приведение (не проверено из-за стирания типа, следовательно, опасно).
Я считаю, что ошибка в том, что тип вашей структуры, Map[String, Any], представляет то, что у вас есть, довольно плохо.
Предположим, что вы делаете вместо
sealed trait Tree
case class Node(items: Map[String, Tree]) extends Tree
case class Leaf(s: String) extends Tree
Вы можете добавить несколько помощников, чтобы упростить объявление Дерева
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: _*))
}
Тогда объявить дерево так же просто, как и в первой версии, но тип гораздо точнее
m = Node("email" -> "a@b.com", "background" -> Node("language" -> "english"))
Затем вы можете добавить методы, например, в 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}
Или, если вы предпочитаете делать это со сгибом
def get(path: String*) = path.foldLeft[Option[Tree]](Some(this)) {
case (Some(Node(map)), p) => map.get(p)
case _ => None
}
Складывание как абстракция над вложенными картами не поддерживается. Кроме того, вы подходите к этому так, чтобы помешать системе типов оказать вам большую помощь. Но, если вы настаиваете, то вам нужна рекурсивная функция:
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
})
}