3

Sometimes I need to create a collection by mapping another one with different type. For example, some function needs List[_] as its parameter type, but I need to produce that by mapping a IndexedSeq[_]:

val r = (1 to n).map { ... }
someFunction(r.toList)

Although I can fulfill that by calling IndexedSeq[_]'s map method first followed by another call to toList, this produces a redundant intermediate collection. Is there any way to avoid this redundant step while still keeping code concise?

Kriem
  • 8,533
  • 14
  • 67
  • 117
Jiangzhou
  • 163
  • 5

3 Answers3

9

Have a look at the full signature for map:

def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[List[A], B, That]): That

The key to this is the implicit CanBuildFrom, which governs how the result collection is generated from the input collection. We can replace the implicit CanBuildFrom with an explicit one that allows us to build a different result collection.

Even better, we don't even have to write this explicit method! It's there already, in the form of scala.collection.breakOut. From the ScalaDoc:

Provides a CanBuildFrom instance that builds a specific target collection (To') irrespective of the original collection (From').

So if we pass in collection.breakOut, we can then specify exactly what we want to the map method:

val x = IndexedSeq(1,2,3,4,5)
x.map[Int, List[Int]](_ * 2)(collection.breakOut)
> res6: List[Int] = List(2, 4, 6, 8, 10) 
Impredicative
  • 4,949
  • 15
  • 41
  • Thanks very much for your very clear answer. Scala's implicit parameter is so powerful and mysterious. I have just read [that](http://stackoverflow.com/questions/5598085/where-does-scala-look-for-implicits) and learnt much. – Jiangzhou Feb 18 '13 at 11:42
3

The answer to your question is collection.breakOut, as stated by om-nom-nom in his comment.

breakOut is an additional argument given to the map method, and - to keep it simple - it allows to return different types of collections from map:

def directMapExample(seq: Seq[Int]): Set[Int] = seq.map(_ * 2)(collection.breakOut)

Note, that the following fails at compilation:

def directMapExample2(seq: Seq[Int]): Set[Int] = seq.map(_ * 2)

For details, see https://stackoverflow.com/a/1716558/298389.

Community
  • 1
  • 1
ziggystar
  • 26,526
  • 9
  • 63
  • 117
0

Using a view might help?

val r = (1 to n).view.map { … }
someFunction(r.toList)

The map function is a strict transformer on Range. However, if you turn it into a view first, then that Range (which is a non-strict collection) will be wrapped inside an object that implements map in a non-strict way. The full range of values would only be produced when calling toList.

Wilfred Springer
  • 10,593
  • 4
  • 49
  • 69