Scala: Как создать карту [K, V] из набора [K] и функцию из K в V?

Каков наилучший способ создать Map[K,V] из Set[K] и функции от K до V?

Например, предположим, что

scala> val s = Set(2, 3, 5)
s: scala.collection.immutable.Set[Int] = Set(2, 3, 5)

и

scala> def func(i: Int) = "" + i + i
func: (i: Int)java.lang.String

Что является самым простым способом создания Map[Int, String](2 -> "22", 3 -> "33", 5 -> "55")

Ответ 1

Вы можете использовать foldLeft:

val func2 = (r: Map[Int,String], i: Int) => r + (i -> func(i))
s.foldLeft(Map.empty[Int,String])(func2)

Это будет работать лучше, чем решение Jesper, потому что foldLeft создает Map за один проход. Йеспер-код сначала создает промежуточную структуру данных, которая затем должна быть преобразована в окончательный Map.

Обновление: Я написал микро-тест, проверяющий скорость каждого из ответов:

Jesper (original): 35s 738ms
Jesper (improved): 11s 618ms
           dbyrne: 11s 906ms
         Rex Kerr: 12s 206ms
          Eastsun: 11s 988ms

Похоже, что все они почти одинаковы, пока вы избегаете создания промежуточной структуры данных.

Ответ 2

Как насчет этого:

(s map { i => i -> func(i) }).toMap

Это отображает элементы s в кортежи (i, func(i)), а затем преобразует полученный набор в Map.

Примечание: i -> func(i) совпадает с (i, func(i)).

dbyrne предлагает сначала создать представление набора (см. его ответ и комментарии), что предотвращает создание промежуточной коллекции, улучшая производительность:

(s.view map { i => i -> func(i) }).toMap

Ответ 3

scala> import collection.breakOut
import collection.breakOut

scala> val set = Set(2,3,5)
set: scala.collection.immutable.Set[Int] = Set(2, 3, 5)

scala> def func(i: Int) = ""+i+i
func: (i: Int)java.lang.String

scala> val map: Map[Int,String] = set.map(i => i -> func(i))(breakOut)
map: Map[Int,String] = Map(2 -> 22, 3 -> 33, 5 -> 55)

scala>

Ответ 4

В дополнение к существующим ответам

Map() ++ set.view.map(i => i -> f(i))

довольно короткий и выполняет, а также более быстрые ответы (fold/breakOut).

(Обратите внимание на представление, чтобы предотвратить создание новой коллекции, а также переназначение).

Ответ 5

Как и во всех великолепных языках, существует миллион способов сделать все.

Здесь стратегия, которая застегивает множество с собой.

val s = Set(1,2,3,4,5)
Map(s.zip(s.map(_.toString)).toArray : _*)

EDIT: (_.toString) можно заменить некоторой функцией, которая возвращает что-то типа V

Ответ 6

В других решениях нет творчества. Здесь моя собственная версия, хотя мне бы очень хотелось избавиться от карты _.head.

s groupBy identity mapValues (_.head) mapValues func

Ответ 7

Без определения func (i: Int) с использованием оператора "string repeating" *:

scala> s map { x => x -> x.toString*2 } toMap
res2: scala.collection.immutable.Map[Int,String] = Map(2 -> 22, 3 -> 33, 5 -> 55)