What are Holes in Arrays?

John Au-Yeung - Jan 23 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

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;
Enter fullscreen mode Exit fullscreen mode

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];
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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);  
}
Enter fullscreen mode Exit fullscreen mode

we get

undefined  
1  
undefined  
2
Enter fullscreen mode Exit fullscreen mode

If we call next to get the next item, as follows,

iter.next();
Enter fullscreen mode Exit fullscreen mode

we get

{value: undefined, done: false}
Enter fullscreen mode Exit fullscreen mode

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);  
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

then we get that the value of arrFrom is

[undefined, 1, undefined, 2]
Enter fullscreen mode Exit fullscreen mode

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  
});
Enter fullscreen mode Exit fullscreen mode

we get the value of arrFrom as

[undefined, "foo"]
Enter fullscreen mode Exit fullscreen mode

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, and some ignore holes.
  • map skips but preserves holes.
  • join and toString treat holes as if they were undefined elements but treat both null and undefined as empty strings.

The following is a full list of methods and how they deal with holes. Each method acts differently.

  • concat — keeps holes
  • copyWithin — holes are copied
  • entries, keys, values — treats holes as undefined
  • every — ignores holes
  • fill — fills holes
  • filter — removes holes
  • find — treats holes as elements
  • findIndex — treats holes as elements
  • forEach — ignores holes
  • indexOf — ignores holes
  • join — converts holes to empty strings
  • lastIndexOf — ignores holes
  • map — preserves holes
  • pop — treat holes as elements
  • push — preserves holes
  • reduce , reduceRight— ignores holes
  • reverse — preserves holes
  • shift — treat holes as undefined
  • slice — preserves holes
  • sort — preserves holes
  • toString — preserves holes
  • unshift — preserves holes
  • values — converts holes to undefined

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .