Why Can’t You .forEach() Over Empty Array Items?
Tags: javascript, loop, foreach, iterator
Published: 2022-09-09 03:51:00

---
title: Why Can’t You .forEach() Over Empty Array Items?
published: true
date: 2022-09-08 05:00:00 UTC
tags: javascript, loop, forEach, iterator
canonical_url: https://macarthur.me/posts/looping-over-empty-array-items
---

Harken back to a time when you needed to execute some code a specific number of times and had some reason to not use a `for` loop. You might’ve made a fresh array with a certain number of items, and then attempted to `.forEach()` over it:

```
const freshArray = new Array(5);

freshArray.forEach(item => {
	console.log(item);
});

```

If you did take that approach, you also might’ve expected to see five individual logs for `undefined`. **But instead, nothing happened,** as if the array didn’t have any items to begin with.

That’s because it technically doesn’t. When a number is passed into the `Array` constructor, no items are actually defined — not even `undefined` ones. Instead, [only the](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Array#parameters)[`length`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Array#parameters)[property is set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Array#parameters), effectively reserving that many seats that may or may not be filled later. You’ve created a [_sparse_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#sparse_arrays)[array.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#sparse_arrays) You can verify that by logging the array itself:

```
const freshArray = new Array(5);

console.log(freshArray);

// [<5 empty items>]

```

When a method on the `Array` prototype (`.map()`, `.forEach()`, etc.) encounters such an array, it’s designed to [skip over any uninitialized or deleted items](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#description). The position of those items doesn’t matter. For example, you could designate “empty” items by inserting commas:

```
const freshArray = [1, 2, , , 5];

console.log("Length:", freshArray.length);

freshArray.forEach(item => {
  console.log(item);
});

// Length: 5
// 1
// 2
// 5

```

And even by directly setting them via index:

```
const freshArray = [];

freshArray[3] = "fourth item!";

console.log("Length:", freshArray.length);

freshArray.forEach((item, index) => {
  console.log(item, index);
});

// Length: 4
// "fourth item!" 3

```

If you’re dead-set on using methods like `.forEach()` or `.map()` with a sparse array, you can reach for something like `.fill()` to [populate each empty item](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill) with something first (even `undefined`).

## Iterators Are Different, Though

That said, even those empty items are accessible using the `Symbol.iterator` method defined on the `Array` prototype. It’s implicitly accessed any time you use a `for` loop.

```
const freshArray = new Array(5);

for (const item of freshArray) {
  console.log(item);
}

// undefined
// undefined
// undefined
// undefined
// undefined

```

It’s also in play under the hood when the spread operator is used, which means you could spread your sparse array, and immediately be able to perform a `.forEach()` on it:

```
const freshArray = new Array(5);

// .forEach() will work now!
[...freshArray].forEach((item, index) => {
  console.log(item, index);
});

// undefined 0
// undefined 1
// undefined 2
// undefined 3
// undefined 4

```

If you wanted, you could use this iteration behavior to make your own variation of `.forEach` that’ll handle empty items (tack on methods to prototypes at your own risk!):

```
Array.prototype.inclusiveForEach = function(callback) {
  return [...this].forEach(callback);
}

const freshArray = new Array(5);

freshArray.inclusiveForEach((item, index) => {
  console.log(item, index);
});

// undefined 0
// undefined 1
// undefined 2
// undefined 3
// undefined 4

```

All of these decisions are up to you, and highly dependent on your circumstances. At the very least, I hope this helps shed some light on why the code behaves the way it does, and offers some considerations to help you move forward.