To iterate over an array-like object, you can do any of the following:
- Use a
for
loop — this is possible because by definition an array-like object haslength
and indexed elements; - Implement the iterable protocol — this would make the array-like object iterable;
- Convert the array-like object to an array — this would allow you to use loops available on an array.
#Use a for
Loop
Since an array-like object has indexed elements and the length
property, you could simply use a for
loop to iterate over its elements, for example, like so:
const obj = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
for (let i = 0; i < obj.length; i++) {
console.log(obj[i]); // output: 'foo', 'bar', 'baz'
}
In cases where the indexes have gaps, it would look like the following:
const obj = { 0: 'foo', 5: 'bar', 7: 'baz', length: 8 };
for (let i = 0; i < obj.length; i++) {
console.log(obj[i]); // output: 'foo', 4 x undefined, 'bar', undefined, 'baz'
}
While a for...in
loop can also be used to iterate over the items in an array-like object, it should be avoided because it will also enumerate its other enumerable properties (such as the length
property, etc.).
#Implement the Iterable Protocol
Iterables (objects that implement the iterable protocol) are data structures that allow access to their elements in a sequential manner. If an array-like object does not implement an iterable protocol, then it won't work with syntaxes that expect iterables (such as for...of
loop, the spread syntax, yield*
, and destructuring assignment). For example:
const obj = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
// TypeError: obj is not iterable
for (const item of obj) {
console.log(item);
}
Conforming to the Iterable Protocol:
An object can be made iterable by conforming to the iterator protocol — i.e. by implementing the next()
method (that returns at least done
and value
properties), for example, like so:
const obj = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
obj[Symbol.iterator] = function () {
let i = -1;
return {
next: () => ({ value: this[++i], done: i === this.length })
};
};
for (const item of obj) {
console.log(item); // output: 'foo', 'bar', 'baz'
}
Since the for...of
loop expects iterable objects, it is able to access the object's Symbol.iterator
function and loop through its properties as per our design.
To give you a better understanding of how the next()
method works, let's substitute the for...of
loop with a while
loop:
// ...
const iterator = obj[Symbol.iterator]();
while (true) {
const result = iterator.next();
if (result.done) {
break;
}
console.log(result.value);
}
// ...
Defining Symbol.iterator
Property Directly Inside the Object:
It is also possible to define the "Symbol.iterator
" property directly inside an object using a computed property like so:
const obj = {
// ...
[Symbol.iterator]() {
let i = -1;
let self = this;
return {
next() {
return { value: self[++i], done: i === self.length }
}
}
},
// ...
};
Using a Generator:
Let's simplify the code from the earlier example using a generator:
const obj = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
obj[Symbol.iterator] = function* () {
for (let i = 0; i < this.length; ++i) {
yield this[i];
}
};
for (const item of obj) {
console.log(item); // output: 'foo', 'bar', 'baz'
}
As you can see from the example above, using a generator as our Symbol.iterator
does not require us to explicitly define a function that returns the "next
" property, which simplifies the code quite a lot.
Defining Symbol.iterator
Property Directly Inside the Object:
It is also possible to define the "Symbol.iterator
" property directly inside an object using a computed property like so:
const obj = {
// ...
*[Symbol.iterator]() {
for (let i = 0; i < this.length; ++i) {
yield this[i];
}
},
// ...
};
#Convert the Array-Like Object Into an Array
It is possible in JavaScript to convert an array-like object to an array. Once you do that, you could then loop over your collection simply as you would with an array using any loop. For example:
const obj = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
const arr = Array.from(obj);
arr.forEach((item) => console.log(item)); // output: 'foo', 'bar', 'baz'
This post was published by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.