Scala лучший способ превратить Коллекцию в Карту под ключ? (2-й вариант) - PullRequest
8 голосов
/ 31 июля 2009

(это вариант этого Q & A )

Скажи, что у меня есть:

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

Я хочу создать карту, для которой ключ - это длина строки, а значение - это коллекция всех строк, имеющих эту длину. Другими словами, учитывая список, мы получили бы:

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

Это работает, но это добавляет много уродства к элегантному ответу Даниэля Спивака, предоставленному на первоначальный вопрос (ссылка выше).

Есть идеи, как улучшить решение для моего варианта?

Спасибо! Шон

Ответы [ 2 ]

19 голосов
/ 31 июля 2009

С Scala 2.8.0:

list.groupBy(_.length)

Это не может быть проще, чем это!

7 голосов
/ 31 июля 2009

Если вы не возражаете против паршивой работы:

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