3

I want to create a map from a collection by providing it a mapping function. It's basically equivalent to what a normal map method does, only I want it to return a Map, not a flat collection.

I would expect it to have a signature like

def toMap[T, S](T => S): Map[T, S]

when invoked like this

val collection = List(1, 2, 3)
val map: Map[Int, String] = collection.toMap(_.toString + " seconds")

the expected value of map would be

Map(1 -> "1 seconds", 2 -> "2 seconds", 3 -> "3 seconds")

The method would be equivalent to

val collection = List(1, 2, 3)
val map: Map[Int, String] = collection.map(x => (x, x.toString + " seconds")).toMap

is there such a method in Scala?

Zoltán
  • 21,321
  • 14
  • 93
  • 134

2 Answers2

3

There is no such single method. As you say, you can use map followed by toMap. If you are concerned about the intermediary list you are creating, you might consider using breakOut as the implicit second argument to map:

import scala.collection.breakOut
val map: Map[Int, String] = collection.map(x => (x._1, x._2.toString + " seconds"))(breakOut)

You can read more about breakOut and the implicit argument of map here.

This method allows you to construct other types that have a suitable CanBuildFrom implementation as well, without the intermediate step:

val arr: Array[(Int, String)] = collection.map(x => (x._1, x._2.toString + " seconds"))(breakOut)

You might also want to consider using views which inhibit creating of intermediary collections:

val m = (List("A", "B", "C").view map (x => x -> x)).toMap

The differences between these approaches are described here.

Finally, there is the mapValues method, which might be suitable for your purposes, if you are only mapping the values of each key-value pair. Be careful, though, since this method actually returns a view, and might lead to unexpected performance hits.

Community
  • 1
  • 1
Ben Reich
  • 16,222
  • 2
  • 38
  • 59
  • Thanks for the answer. I wasn't specifically worried about the intermediary collection. It just felt like something that may have been a part of the standard library, but I couldn't find it. – Zoltán Dec 17 '14 at 08:50
  • I agree that it is missing. In .NET, the `ToMap` method takes arguments that allow you to define the key and value using lambdas, which is often useful. `mapValues` is a nice helper, though. – Ben Reich Dec 17 '14 at 13:58
3

scalaz has a fproduct method for Functors which returns things in the right shape for calling .toMap on the result:

scala> import scalaz._,Scalaz._
import scalaz._
import Scalaz._

scala> val collection = List(1, 2, 3)
collection: List[Int] = List(1, 2, 3)

scala> collection.fproduct(_.toString + " seconds").toMap
res0: scala.collection.immutable.Map[Int,String] = Map(1 -> 1 seconds, 2 -> 2 seconds, 3 -> 3 seconds)
stew
  • 11,276
  • 36
  • 49