Array iteration and holes in JavaScript

The blog post describes how various functions and methods that deal with arrays are affected by holes [1].



Running example



All of the code below assumes that the following variable declaration has been made:

var arr = [ 'a',, 'b' ];

Above, we have created an array that has the element 'a' at index 0, a hole (no element at all) at index 1 and the element 'b' at index 2.

Preliminaries



Trailing holes



For this blog post, it is important to keep in mind that JavaScript ignores trailing commas in arrays. That means that you need to write two commas to create a trailing hole:

> [ 'a', 'b', ]
[ 'a', 'b' ]
> [ 'a', 'b',, ]
[ 'a', 'b', , ]


Copying arrays



We use slice() to create a copy arr2 of an existing array arr:

var arr2 = arr.slice();

The right-hand side of the above assignment is equivalent to:

arr.slice(0, arr.length)


Array methods



forEach() skips holes:

> arr.forEach(function (x,i) { console.log(i+'.'+x) })
0.a
2.b

every() also skips holes (similarly: some()):

> arr.every(function (x) { return x.length === 1 })
true


map() skips, but preserves holes:

> arr.map(function (x,i) { return i+'.'+x })
[ '0.a', , '2.b' ]


filter() eliminates holes:

> arr.filter(function (x) { return true })
[ 'a', 'b' ]


join() converts holes and undefineds to empty strings.

> arr.join('-')
'a--b'
> [ 'a', undefined, 'b' ].join('-')
'a--b'


Other array methods



All other array methods preserve holes. For example, sort():


> var arr2 = arr.slice()
> arr2.sort()
[ 'a', 'b', , ]


Loops



The for loop does not access arrays, the for-in loop correctly lists property keys:

> var arr2 = arr.slice()
> arr2.foo = 123;
> for(var key in arr2) { console.log(key) }
0
2
foo


Function.prototype.apply()



apply() treats holes as undefined elements. That allows you to easily create an array with a given number of undefineds [1]:

> Array.apply(null, Array(3))
[ undefined, undefined, undefined ]

apply() nicely plugs holes of empty arrays. You cannot, however use it to do so for arbitrary arrays, which may or may not contain holes. For example, the arbitrary array [2] does not contain holes, so apply() should return it “unchanged”. But it doesn’t, it creates an empty array whose length is 2 (because the function Array() interprets single numbers as array lengths, not as array elements).

> Array.apply(null, [2])
[ , ,]



Conclusion and recommendation



We have seen that JavaScript handles holes in a variety of ways. Thankfully, you normally don’t need to know how holes are handled, because arrays with holes should be avoided. Holes affect performance negatively and are rarely useful.

Further reading




  1. JavaScript: sparse arrays vs. dense arrays


    What are holes and how do they differ from elements that are undefined?

  2. Iterating over arrays and objects in JavaScript


    General information on iterating over arrays.

Comments

Popular posts from this blog

Steve Lopez and the Importance of Newspapers

Ideas for fixing unconnected computing

Omar to kill me