如何使用第一个地图的值检索嵌套地图的值?

时间:2019-07-02 05:58:53

标签: algorithm scala functional-programming hashmap pattern-matching

我有一个Map [String,String]类型的条目映射和一个Map [String,Map [String,String]]类型的查找映射。我的目标是在第一张地图中查找第二张地图中的匹配项,一旦有了键匹配项,就必须用第二张字典的查找替换第一张字典中的值。该示例将清除所有问题。

到目前为止,我已经提出了一种算法,但是我得到了Some(e)和Some(t)的一个奇怪答案。

第一个条目映射如下:

val entries = Map("fruit" -> "aaa", "animal" -> "bbb", "person" -> "jjj")

第二张地图如下:

val lookup = Map("fruit" -> Map("ccc" -> "orange", "aaa" -> "apple"),
                 "animal" -> Map("zzz" -> "dog", "bbb" -> "cat"))

我期望的结果如下(注意:由于没有匹配项,因此未正确包含“人”):

val result = Map("fruit" -> "apple", "animal" -> "cat")

我想出的算法如下:

val res = for{ (k, ev) <- entries 
      lv <- lookup.get(k).get(ev)} yield (k, lv)

此算法给我以下结果,我不知道为什么:

Map(fruit -> e, animal -> t)

e和t来自哪里?

5 个答案:

答案 0 :(得分:5)

将其细分为各个组成部分。

for {
  (k,v1) <- entries
  submap <- lookup.get(k)
  v2     <- submap.get(v1)
} yield (k,v2)
//res0: immutable.Map[String,String] = Map(fruit -> apple, animal -> cat)

不确定错误输出中et的来源。


好的,我知道了。 lv <- lookup.get(k).get(ev)分别遍历"apple""cat"的每个字母,但由于Map中每个键只能有一个键->值对,因此只有最后一个信被保留。

答案 1 :(得分:1)

这将起作用:

val res = for{ 
    (k, ev) <- entries
    l1 <- lookup.get(k)
    l2 <- l1.get(ev)
} yield { 
    (k, lv)
}

问题说明:

lookup.get(k).get(ev) //this returns a String <- reason: not known

x <- "apple" //x is now a List[Char] -> List('a','p','p','l','e')

现在:

map += ("animal" -> 'a')
map += ("animal" -> 'p')
map += ("animal" -> 'p')
map += ("animal" -> 'l')
map += ("animal" -> 'e')
map += ("fruit" -> 'c')
map += ("fruit" -> 'a')
map += ("fruit" -> 't')

将导致:

Map("animal" -> 'e', "fruit", 't')

答案 2 :(得分:1)

yield的文档说明了为什么会发生这种情况:https://docs.scala-lang.org/tutorials/FAQ/yield.html

示例2中的页面指出:

for(x <- c1; y <- c2; z <- c3) yield {...}
is translated into
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))

因此,您的理解力会转换为:

entries.flatMap({case (k, ev) => lookup.get(k).get(ev).map(lv => (k, lv)) })

此处的关键是“ apple”和“ cat”中的每一个都是map完成的字符串(即,您要遍历这些单词的每个字符)。由于Map正在用("fruit" -> "a") ... ("fruit" -> "e")更新,因此只有(fruit -> e)是显而易见的,最终结果是您看到的:

Map(fruit -> e, animal -> t)

一些获得预期结果的方法:

for{ (k, ev) <- entries ;lv <- lookup.get(k)} yield (k, lv.get(ev).get)

或者完全避免连锁理解,而只使用常规的map

entries.map({case (k, v) => (k, lookup.get(k).get(v))})

这两个结果均产生Map(fruit -> apple, animal -> cat),但请注意,这些解决方案无法处理get返回None(即查找映射缺少该键)的情况

答案 3 :(得分:0)

您也可以尝试以下方法获取输出

val output = entries.map{
  case (k,v) =>
    k -> lookup.get(k).flatMap(_.get(v)).getOrElse(v)
}

这会将您的输出作为

output: scala.collection.immutable.Map[String,String] = Map(fruit -> apple, animal -> cat)

答案 4 :(得分:0)

内联解决方案:

val res =  entries.flatMap(e => lookup.get(e._1).flatMap(_.get(e._2).map(e._1->_)))

输出:

res: scala.collection.immutable.Map[String,String] = Map(fruit -> apple, animal -> cat)