Scala将集合转换为键值对Map的最佳方式是什么?(第二种变体)

8
(这是对此问答的一种变体)
假设我有以下内容:
List( "foo", "bar", "spam" )

我希望创建一个Map,其中键为字符串的长度,值为所有具有该长度的字符串集合。换句话说,给定上述列表,我们将得到:

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提供的原始问题的优雅答案变得很丑陋(上面引用了原始问题)。

有没有什么想法来改进我的变体解决方案?

谢谢! Sean

2个回答

19

使用Scala 2.8.0版本:

list.groupBy(_.length)

没有比这更简单的了!


我认为这很优雅,但我不理解为什么需要排序。你能解释一下吗?谢谢。 - agilefall

7

如果你不介意性能差:

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 length”缩短为“val l”,我不认为最后一个变量会很丑陋。在Scala中,单字母变量通常以类似于数学公式的风格使用,即声明占位符变量的含义,然后只是使用它。由于这导致非常简短的表达式(通常只有一行),因此在其他语言中可能被认为是加密的短名称在实践中并不成问题。 - Palimondo
@Palimondo,让我烦恼的不是长度。我不喜欢将计算分成两个语句。不幸的是,Scala不能像Haskell那样优化s.length以重用该值。 - Daniel C. Sobral

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接