【问题标题】:Scala best way of turning a Collection into a Map-by-key? (2nd variant)Scala 将 Collection 转换为 Map-by-key 的最佳方式? (第二个变种)
【发布时间】:2010-11-15 15:10:12
【问题描述】:

(这是this Q&A的变体)

说我有这个:

List( "foo", "bar", "spam" )

我想创建一个 Map,其键是字符串的长度,值是具有该长度的所有字符串的集合。换句话说,给定 about 列表,我们会得到:

Map( 3 -> List(foo, bar), 4 -> List(spam) )

我为此编写的代码是:

list.foldLeft(Map[Long, List[String]]()) {
  (m, s) => m(s.length) = s ::
     ( if ( m.contains(s.length) ) m(s.length)
       else Nil )
}

这可行,但它给 Daniel Spiewak 为原始问题提供的优雅答案增加了很多丑陋(参考上文)。

任何想法如何改进我的变体的解决方案?

谢谢! 肖恩

【问题讨论】:

    标签: list scala map scala-collections


    【解决方案1】:

    使用 Scala 2.8.0:

    list.groupBy(_.length)
    

    没有比这更简单的了!

    【讨论】:

    • 我认为这很优雅,但我不明白为什么需要排序。你能解释一下吗?谢谢。
    【解决方案2】:

    如果你不介意糟糕的表现:

    val list = List( "foo", "bar", "spam" )
    val keyValue = for (length <- list map (_ length) removeDuplicates;
                        strings = list filter (_.length == length)) 
                   yield (length -> strings)
    val map = Map(keyValue: _*)
    

    问题是每个不同长度的列表都会被再次读取。

    现在,关于你的版本的丑陋,也许这会有所帮助:

    list.foldLeft(Map[Long, List[String]]()) {
      (m, s) => m(s.length) = s :: m.getOrElse(s.length, Nil)
    }
    

    更好?它仍然不是很好,因为你得到了两次长度。这个没有这个问题,只是丑了点:

    list.foldLeft(Map[Long, List[String]]()) {
      (m, s) => val length = s.length; m(length) = s :: m.getOrElse(length, Nil)
    }
    

    【讨论】:

    • 如果 val 长度缩短为 val l,我不觉得最后一个变体难看。 scala 中的单字母变量通常以类似于数学公式的风格在 scala 中使用,即您声明占位符变量的含义,然后直接使用它。因为这会导致表达式非常短(通常是一行),所以在其他语言中会被认为是晦涩难懂的短名称在实践中不是问题。
    • @Palimondo 这不是让我烦恼的长度。我不喜欢在两个语句中破坏计算。不幸的是,Scala 无法像 Haskell 那样优化 s.length 以重用该值。
    猜你喜欢
    • 2010-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-20
    • 2019-08-08
    • 1970-01-01
    相关资源
    最近更新 更多