I understand that the basic for...of
syntax in JavaScript looks like this:
for (let obj of myArray) {
// ...
}
But how do I get the loop counter/index when iterating with this syntax?
(With the same question applying to for...in
notation for iterating over object property names)
I know I can use an explicit loop counter like:
for (let i = 0; i < myArray.length; i++) {
const obj = myArray[i];
console.log(i);
}
Or manually track the index outside of the loop:
let i = 0;
for (let obj of myArray) {
console.log(i);
i++;
}
But I would rather use the simpler for...of
loop, I think they look better and make more sense.
As an example of a language that lets you do this, in Python it's as easy as:
for i, obj in enumerate(my_array):
print(i)
for…in
iterates over property names, not values (and did so in an unspecified order up until ES2020*). You shouldn’t use it to iterate over arrays. For them, there’s ES6’s Array.prototype.entries
, which now has support across current browser versions:
const myArray = [123, 15, 187, 32];
for (const [i, value] of myArray.entries()) {
console.log(`${i}: ${value}`);
}
// 0: 123
// 1: 15
// 2: 187
// 3: 32
.as-console-wrapper { max-height: 100% !important; top: 0; border-top: 0 !important; }
Or, for extended compatibility with older browsers, there’s ES5’s forEach
method that passes both the value and the index to the function you give it:
myArray.forEach(function (value, i) {
console.log('%d: %s', i, value);
});
For iterables in general (where you would use a for…of
loop rather than a for…in
), iterator helpers are now in the language. You can use Iterator.prototype.forEach
to iterate over an entire iterable with an index:
function* fibonacci() {
let a = 0;
let b = 1;
for (;;) {
yield a;
[a, b] = [b, a + b];
}
}
fibonacci().take(10).forEach((x, i) => {
console.log(`F_${i} = ${x}`);
});
.as-console-wrapper { max-height: 100% !important; top: 0; border-top: 0 !important; }
More generally, Iterator#map
can associate the values yielded by an iterator with their indexes:
fibonacci().map((x, i) => [i, x])
Not every iterable (or iterator!) is an Iterator
, but you can convert every iterable to an Iterator
with Iterator.from
.
Without support for iterator helpers, you can use a generator function instead:
function* enumerate(iterable) {
let i = 0;
for (const x of iterable) {
yield [i, x];
i++;
}
}
for (const [i, obj] of enumerate(myArray)) {
console.log(i, obj);
}
If you actually did mean for…in
– enumerating properties – you would need an additional counter. Object.keys(obj).forEach
could work, but it only includes own properties; for…in
includes enumerable properties anywhere on the prototype chain.
* The order is still unspecified under certain circumstances, including for typed arrays, proxies, and other exotic objects, as well as when properties are added or removed during iteration.