4

The following code creates a temporary Vector:

0.to(15).map(f).toArray
^^^^^^^^
Sequence
^^^^^^^^^^^^^^^
    temp Vector
^^^^^^^^^^^^^^^^^^^^^^^
                  Array

The following code creates a temporary Array:

0.to(15).toArray.map(f)
^^^^^^^^
Sequence
^^^^^^^^^^^^^^^
     temp Array
^^^^^^^^^^^^^^^^^^^^^^^
                  Array

Is there a way to map f over the Sequence and directly get an Array, without producing a temporary?

fredoverflow
  • 256,549
  • 94
  • 388
  • 662

3 Answers3

5

You can use breakOut:

val res: Array[Int] = 0.to(15).map(f)(scala.collection.breakOut)

or

0.to(15).map[Int, Array[Int]](f)(scala.collection.breakOut)

or use view:

0.to(15).view.map(f).to[Array]

See this document for more details on views.

Ziyang Liu
  • 810
  • 4
  • 10
  • 1
    Can you briefly explain what `breakOut` does? – fredoverflow Sep 06 '17 at 14:50
  • Also, it seems `0.to(15).map[Int, Array[Int]](f)` already works without the additional `(scala.collection.breakOut)` parameter list. – fredoverflow Sep 06 '17 at 15:03
  • In short `breakOut` is a method that finds the correct `CanBuildFrom` instance based on the desired input and output type, which the compiler otherwise may not be able to correctly infer. With the right `CanBuildFrom` instance you can create a new type of collection while mapping over the original collection. More details at https://stackoverflow.com/questions/1715681/scala-2-8-breakout – Ziyang Liu Sep 06 '17 at 15:07
  • `0.to(15).map[Int, Array[Int]](_ + 1)` doesn't work for me in Scala 2.12.3 as the compiler complains about not being able to find `CanBuildFrom`. – Ziyang Liu Sep 06 '17 at 15:08
  • In my case, `f` is a concrete function, so the compiler already knows the type. Does it work with `(_ + 1: Int => Int)` or something similar? – fredoverflow Sep 06 '17 at 15:16
  • Nope - `0.to(15).map[Int, Array[Int]]((_ + 1): Int => Int)` doesn't compile with the same error as `0.to(15).map[Int, Array[Int]](_ + 1)` that basically says `CanBuildFrom` cannot be found. – Ziyang Liu Sep 06 '17 at 15:35
  • You are correct. I just checked, and it fails to compile in 2.12.3 (but compiles successfully in 2.11.11)... – fredoverflow Sep 06 '17 at 18:46
1
(0 to 15).iterator.map(f).toArray
Dima
  • 39,570
  • 6
  • 44
  • 70
1

If you have the hand on how to build the initial Range, you could alternatively use Array.range(start, end) from the Array companion object which creates a Range directly as an Array without cast:

Array.range(0, 15) // Array[Int] = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)

which can be mapped from an Array to an Array as such:

Array.range(0, 15).map(f) // Array[Int] = Array(0, 2, 4, 6, 8, 10, 12, 14, ...)

Note: Array.range(i, j) is an equivalent of i until j (i to j+1) and not i to j.


Even shorter and in a single pass, but less readable, using Array.tabulate:

Array.tabulate(15)(f) // Array[Int] = Array(0, 2, 4, 6, 8, 10, 12, 14, ...)
Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190