One special feature of JavaScript arrays is that not every slot in the array has to be filled with values. This means that we can skip values as follows:
let arr = [];
arr[1] = 1;
arr[10] = 10;
We don’t have to worry about setting values for other slots of the array.
There’s also an alternative syntax for doing the above, by writing:
[, 1, , 2];
What we have above are called holes of an array, where we have nothing between the commas. An array with holes is called a sparse array.
In this piece, we’ll look at how holes in arrays are handled in JavaScript.
Checking for Values in Arrays
We can check for holes in arrays with the in
operator. To do this, we can write something like the following code:
const arr = [, 1, , 2];
1 in arr;
We should get true
returned from the last line since 1 is in the array.
ES6 treat holes in arrays as undefined
entries. So if we want to check for holes in the array, check for undefined
.
Iterators and generators also treat holes as undefined
. For example, if we have
const arr = [, 1, , 2];
const iter = arr[Symbol.iterator]()
for (let a of iter) {
console.log(a);
}
we get
undefined
1
undefined
2
If we call next
to get the next item, as follows,
iter.next();
we get
{value: undefined, done: false}
for the first entry.
Likewise, if we have the given generator,
function* generator () {
const arr = [, 1, , 2];
for (let a of arr) {
yield a;
}
}
for (let a of generator()) {
console.log(a);
}
we get the same thing.
Array.from()
Array.from()
treats holes as undefined
like with iterators and generators.
For example, if we have
const arr = [, 1, , 2];
const arrFrom = Array.from(arr);
then we get that the value of arrFrom
is
[undefined, 1, undefined, 2]
Likewise, if we create an array from array-like objects, where we have non-negative integers as keys and a length property with a non-negative number as a value, missing entries are also treated as undefined
.
For example, if we run
const arrFrom = Array.from({
1: 'foo',
length: 2
});
we get the value of arrFrom
as
[undefined, "foo"]
How Array.prototype Methods Treat Holes
The behavior of these methods differs with different versions of JavaScript. In ES5, they’re the following:
forEach
,filter
,every
, andsome
ignore holes.map
skips but preserves holes.join
andtoString
treat holes as if they wereundefined
elements but treat bothnull
andundefined
as empty strings.
The following is a full list of methods and how they deal with holes. Each method acts differently.
concat
— keeps holescopyWithin
— holes are copiedentries
,keys
,values
— treats holes asundefined
every
— ignores holesfill
— fills holesfilter
— removes holesfind
— treats holes as elementsfindIndex
— treats holes as elementsforEach
— ignores holesindexOf
— ignores holesjoin
— converts holes to empty stringslastIndexOf
— ignores holesmap
— preserves holespop
— treat holes as elementspush
— preserves holesreduce
,reduceRight
— ignores holesreverse
— preserves holesshift
— treat holes asundefined
slice
— preserves holessort
— preserves holestoString
— preserves holesunshift
— preserves holesvalues
— converts holes toundefined
When arrays are sparse, they have holes. They’re iterated through as undefined
, but each array prototype method treats holes differently, so we have to be careful when defining arrays with holes and dealing with them.
We can use the in
operator to check if a given entry is in an array.
Array.from()
also treats holes as undefined
and turns holes into undefined
when converting arrays or array-like objects with holes into arrays.