Scala best way of turning a Collection into a Map-by-key? (2nd variant)

8 scala list map scala-collections

(This is a variant to this Q&A)

Say I have this:

List( "foo", "bar", "spam" )
Run Code Online (Sandbox Code Playgroud)

I want to create a Map for which the key is the length of the String and the value is a Collection of all the Strings that have that length. In other words, given the about List, we'd get:

Map( 3 -> List(foo, bar), 4 -> List(spam) )
Run Code Online (Sandbox Code Playgroud)

The code I've written to do this is:

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

This works, but it adds a lot of ugliness to the elegant answer Daniel Spiewak provided to the original question (referenced above).

Any ideas how to improve the solution for my variant?

Thanks! Sean

Wal*_*ang 19

使用Scala 2.8.0:

list.groupBy(_.length)
Run Code Online (Sandbox Code Playgroud)

它不能比这更简单!


Dan*_*ral 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: _*)
Run Code Online (Sandbox Code Playgroud)

问题是每个不同的长度再次读取列表.

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

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

更好?它仍然不太好,因为你得到两倍的长度.这一个没有这个问题,但它有点丑陋:

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