【问题标题】:Scala: flip key-value mapScala:翻转键值映射
【发布时间】:2018-04-11 08:40:25
【问题描述】:

我需要转换以下地图:

Map(1 -> Seq("A", "E"), 2 -> Seq("D", "G"))

到:

Map("a" -> 1, "d" -> 2, "e" -> 1, "g" -> 2)

我该如何解决?

谢谢!!!

【问题讨论】:

    标签: scala collections


    【解决方案1】:

    假设m 是您的输入映射,

    for ((v, ks) <- m; k <- ks) yield (k.toLowerCase, v)
    

    产生:

    Map(a -> 1, e -> 1, d -> 2, g -> 2)
    

    这实际上是配方的 1:1 翻译:“对于原始映射中的每一对整数值 v 和字母列表 ks,从 ks 中获取每个字母 k 并添加将(k, v) 与新地图配对”。

    【讨论】:

    • 老实说,我对脱糖显式 flatMapmap 的突然流行感到有些惊讶:我们已经有了简短的 for-syntax ,为什么不使用它?就好像 Python 用户会突然开始用__iter__-方法重写他们所有的列表理解......我很困惑。 O_o?
    • 我同意for/yield 在许多(如果不是大多数)情况下更容易理解。也许flatMapmap 方法在几乎所有类型的Scala 集合/容器中的突出地位可能有助于以某种方式宣传自己。或者,也许人们只是不想成为slapped 因为不使用它。
    • @LeoC 据我了解this thread 例如,它更多的是用flatMap 替换嵌套的match { case Some(...) =&gt; { ... match case Some(...) =&gt; ... } ... case None =&gt; }-thingies 的塔,然后最终用for-comprehensions,因为它们没有t 为每个新的绑定变量引入一级缩进......这对for-syntax imho 的影响较小。我不知道,for 在这种特殊情况下似乎更合适。
    • 也许我应该强调,我并不坚持在任何地方都使用for 而不是flatMap,事实上,我投票率最高的问题之一正是关于a case where for-comprehension fails badly, but flatMap works nicely。但在这种特殊情况下,我看不出有任何理由不使用for-yield
    【解决方案2】:

    使用flatMap 并使用映射将内部列表转换为所需的形式。

    警告:序列不能有重复。

    scala> val map = Map(1 -> Seq("A", "E"), 2 -> Seq("D", "G"))
    map: scala.collection.immutable.Map[Int,Seq[String]] = Map(1 -> List(A, E), 2 -> List(D, G))
    
    scala> map.flatMap { case (k, v) => v.map(_.toLowerCase -> k) }
    res0: scala.collection.immutable.Map[String,Int] = Map(a -> 1, e -> 1, d -> 2, g -> 2)
    

    【讨论】:

    • toMap 是多余的。如果回调中的返回值是元组的可迭代,则在 Map 上调用的 flatMap 将是 Map
    • 与其他答案相同:如果您已经(无论出于何种原因)避免使用for-comprehension 语法,如果已经有map,是否真的有必要调用变量mapflatMap 在表达式中?无论我在哪里看到一个孤立的map,我都会自动开始搜索集合c,在该集合上应该调用方法c.map...
    【解决方案3】:

    您可以执行以下操作

    val m = Map(1 -> Seq("A", "E"), 2 -> Seq("D", "G"))
    
    println(m.flatMap(x => x._2.map(y => (y.toLowerCase, x._1))))
    

    你会得到

    Map(a -> 1, e -> 1, d -> 2, g -> 2)
    

    【讨论】:

    • ...那为什么不一路写map.flatMap(map =&gt; map._2.map(flatMap =&gt; (flatMap.toLowerCase, map._1)))呢? ;)
    • 是的,我知道...但是明确写出的map-flatMap-方法调用本身已经有些密集(这就是发明for-syntax的原因),并添加额外的变量名称也称为map 似乎并没有使它更容易阅读。
    • 你想让我删除我的答案@AndreyTyukin ;) 我已经对你的答案投了赞成票,但我无法使用 for 循环回答同样的问题
    • 不,为什么...我只是想指出变量名称的选择有些令人困惑。您的答案也没有具体说明,我在所有将输入Map“地图”称为“地图”的提案中都指出了这一点。感谢您更新答案,我发现它已经好多了;)
    【解决方案4】:

    另一个答案是正确的,但对于刚接触 scala 的人来说,他们可能很难理解。 下面简单易懂

    val z = Map(1 -> Seq("A", "E"), 2 -> Seq("D", "G"))
    z.map{case (i,l) => l.map(s=> (s.toLowerCase->i))}.flatten.toMap
    

    【讨论】:

    • 鉴于flattenflatMap(identity) 相同,您可以将其简化为更简单的z.flatMap{ case (i, l) =&gt; l.map(s =&gt; (s.toLowerCase -&gt; i)) }.toMap,因为现在您可以删除toMap,所以您最终会得到@ pamu 的解决方案,如果您现在将flatMap { case (i, l) =&gt; ...} 进一步简化为简洁的for-comprehension,您将获得for ((i,l) &lt;- z; s &lt;- l) yield (s.toLowerCase -&gt; i)。老实说,我不知道为什么每个人都认为将 for-comprehensions 扩展到低级 flatMap-implementation 细节会使任何事情变得更简单......
    • 这正是 scala 对新程序员有问题的原因,他们无法执行或理解您刚刚描述的流程。无论如何,你给它很好,所以也许其他人可以理解其他答案
    • 我认为新程序员一开始就没有必要接触到 for-comprehensions 的脱糖。 “对于原始地图中的每个数字和序列,从序列中取出每个字母,并将对(字母,数字)添加到新地图中” - 尽管我的英语并不完美,但即使对于非-程序员。这直接转化为for ((number, sequence) &lt;- m; letter &lt;- sequence) yield (letter, number)。在这种情况下,flatMap 是一个无关紧要的实现细节。
    【解决方案5】:

    我们也可以使用collect来实现这个

    scala> val map =Map(1 -> Seq("A", "E"), 2 -> Seq("D", "G"))
    map: scala.collection.immutable.Map[Int,Seq[String]] = Map(1 -> List(A, E), 2 -> List(D, G))
    
     scala> map.collect{case (k,v) => v.map(_.toLowerCase -> k)}.flatten.toMap
    //res2: scala.collection.immutable.Map[String,Int] = Map(a -> 1, e -> 1, d -> 2, g -> 2)
    

    【讨论】:

    • 在这种情况下使用collect等同于使用map,而collect { ... }.flatten.toMap在这种情况下等同于仅使用flatMap
    猜你喜欢
    • 1970-01-01
    • 2018-07-23
    • 2012-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多