Entry data

In a recent project I stumbled on a very specific issue, and was reminded of a JavaScript feature I rarely see.

For context, I managed to fill an array with objects depicting different shapes, and started to go through the list with a favorite mechanism, a for...of loop.

for (const shape of shapes) {
	// ...
}

Soon, however, I realized I could benefit from knowing the index, to offset the shapes one after the other. In hindsight, it might have been better to remodel the data, to incorporate the index in the collection, but let me continue to close the argument.

The for...of loop is extremely handy to consider the items, but unfortunately, the incrementing number is not as readily available. That being said, you don’t have to fall back to a faithful, regular loop, updating a counter variable and extracting the items through the round figure.

for (let index = 0; index < shapes.length; index++) {
	const shape = shapes[index];
	// ...
}

In a welcome twist, for...of remains useful with a method present on every array: .entries.

for (const [index, shape] of shapes.entries()) {
	// ...
}

The solution may look contrived, but as you break down the logic and pause to appreciate the contribution of the method, the snippet starts to make much more sense.

.entries returns an array iterator, an object equipped with a few helper functions to iterate through the array. If you store a reference to this construct, for instance, the next function prompts a sweet precious value.

const arrayIterator = shapes.entries();
arrayIterator.next();

An object with two most useful keys: value and done.

Console arrayIterator.next() Object { value: [0, firstShape] done: false }

value refers to an array, including the index of the current item and the item itself. done, on the other hand, describes a boolean, and whether or not the iterator has fulfilled its task, it is complete.

You can repeat the next function again and again, and manually find the second, third, fourth value, in index and accompanying item. Until you reach the end and finally, one last object.

Console arrayIterator.next() Object { value: undefined done: true }

As the iterator comes to a close, value is undefined, while done, fittingly true.

Most fittingly, for...of loop is able to iterate through more than arrays, like NodeLists returned as you study the DOM.

const listItems = document.querySelectorAll('ul li');
for (const listItem of listItems) {
	// ...
}

And topically, through array iterators.

for (const value of shapes.entries()) {
	// ...
}

Rather conveniently, the loop continues as much as necessary, until the first object with the positive boolean. In the meantime, it refers to the data contained in the value property, to the array of index and item. Destructure the two and, once more, you’ve reached the solution.

for (const value of shapes.entries()) {
	const [index, shape] = value;
}

Of course nothing stops you from extracting the values directly in the loop. At this point you repeat the code from above, but instead of scrolling back up feel free to explore another route, and find a much better way. Be it a different function, or a radically different architecture.