Sean Crotty
Sean Crotty

Reputation:

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

(This is a variant to this Q&A)

Say I have this:

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

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) )

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 )
}

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

Upvotes: 8

Views: 1932

Answers (2)

Walter Chang
Walter Chang

Reputation: 11596

With Scala 2.8.0:

list.groupBy(_.length)

It can not get any simpler than that!

Upvotes: 19

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297195

If you don't mind lousy performance:

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: _*)

The problem is that the list is read again for each different length.

Now, about the uglyness of your version, perhaps this helps:

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

Better? It's still not quite good because you get the length twice. This one doesn't have this problem, but it's slightly uglier:

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

Upvotes: 7

Related Questions