How to quickly create an array of numbers in JavaScript (and how it works)

Intro

Have you ever found yourself wanting to create an array populated with numbers ranging from 0 and up? It is admittedly pretty easy to just type it out:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

But what if you want an array that’s a 100 or 1000 items long? Well, as good programmers we would of course use a loop to accomplish such a menial task:
const myArray = []
for (let i = 0; i < 1000; i++) {
  myArray.push(i)
}

But what if I put on my best Lawrence Fishburne impression and told you that there’s a more concise and cool way to do it? Well, there is. And it looks like this:
[...Array(1000).keys()]

While cool, this is not exactly clean, so let’s break it down, starting from the inside out.

The breakdown

First of all ‘Array()’ is the same thing as ‘new Array()’. When passed in a number the result will be an array of the specified length, but completely empty.

Next we have Array.prototype.keys(). This returns an Array Iterator containing the keys of all the indexes in the array. Even though the array is empty, the empty slots still have an internal key (0, 1, 2… etc.). This key is used when the array needs to be iterated over, for instance using for…of.

Array(1000).keys()

So now we have an array iterator containing the keys 0 to 999. How can we make these keys into an array of their own? Well, when making a new array you can pass in any number of parameters and these parameters will form the content of the new array. So, using the spread operator we can turn the iterable into a bunch of parameters, and voila! We pass them into yet another new array and there we have it:
Array(...Array(1000).keys())

Instead of using the ‘new Array()’-syntax we can substitute the surrounding call with simple brackets (in the same way as ‘new Array(‘a’, ‘b’, ‘c’) is the same as [‘a’, ‘b’, ‘c’] )
[...Array(1000).keys()]

A word of warning?

If you’re like me and like coding JavaScript without using semicolons (I do it mostly because some people get hilariously upset when they see it) there are a couple of gotchas that you should watch out for (and that any sane code linter will warn you about). One of these is that you shouldn’t start a new line with ‘(‘ or ‘[‘ (and a few other symbols) which you might be doing if you make use of this kind of array creation. Though only if you don’t store the result of the array creation in a variable… and why would you do that?

Performance

So is there a noticeable difference in performance between these two methods of array creation? Here is the code I’m running, on an Ubuntu machine:

console.time('for-loop')
const array = []
for (var i = 0; i < 10000000; i++) {
  array.push(i)
}
console.timeEnd('for-loop')

console.time('cool way')
const coolArray = [...Array(10000000).keys()]
console.timeEnd('cool way')

The result was that Chrome had more difficulty with the cool way and the regular for loop finished in about 1/3 the time. Firefox, however, was equally fast in both situations. Regardless of method, this is not an expensive operation, and i had to crank the numbers way up before any kind of human-noticable delay made itself present.

Update!

I just stumbled onto some mind-blowingly cool code to make the creation of array ranges super easy in JavaScript. Check this out:

Number.prototype[Symbol.iterator] = function* () {
  for (let i=0; i < this; i++) {
    yield i;
  }
}

And then:

[...1000]

How clean is that? Amazingly if you ask me. But the implementation might be a bit hard to understand, so let’s take a closer look.

If you were to access the value of Symbol.iterator on the prototype of an array or a string you would get a function that is used when this data structure is iterated over. This function is what makes things like the spread operator and ‘for…of’ possible. Numbers, however, don’t have a function like this; they are not iterable. Well, not until we make them so! The function we assign to Number.prototype[Symbol.iterator] is a generator function. When JavaScript tries to iterate over a piece of data it calls this method, then calls .next() on the resulting object for as long as it can, and uses each value yielded in each iteration. The function we’re defining here yields 0 on it’s first .next()-call, and then on subsequent calls it yields the numbers one by one until the condition of the for loop has been met. The value undefined is then yielded and the iteration cycle ends. This means that whenever JavaScript now tries to iterate over a number it will get the values 0 to number-1 to work with. Example:

for (x of 5) { console.log(x) }
// 0
// 1
// 2
// 3
// 4

And the case most relevant to this article:
[...5]
// [0, 1, 2, 3, 4]


Performance wise though, this is noticeably slower than both other methods when using very large arrays.

Leave a Reply

Your email address will not be published. Required fields are marked *