Reputation: 2735
I have been trying to make an iterator using a plain function, without a generator or using the Symbol.iterator
protocol for academic purposes. For that, I have made a function that returns an object with a next
parameter, but trying to run it as the iterable
argument of an for...of
loop yields unwanted results.
Here is my code so far, which I copied from the Iterators and Generators page on MDN:
function iterateThis(arr){
let i = 0;
return {
next: function() {
return i < arr.length ?
{value: arr[i++], done: false} :
{done: true};
}
};
}
If I try to run it like so:
const iterable = iterateThis([1,2,3,4,5]);
for(item in iterable){
console.log(item);
}
On the console, I just get a single result: next
.
Am I doing something wrong in the creation of the function iterateThis
? Or is for...of
only designed to work with generators and the Symbol.iterator
property?
Executed on Node v8.11.1
Upvotes: 2
Views: 554
Reputation: 350996
Your function correctly returns an iterator, but there are two issues in the rest of your code:
iterateThis
returns an iterator, but not an iterable. This also means name iterator
for your variable is misleading.for..in
syntax (which iterates properties), while you would need for..of
(if the object were iterable).Since ECMAScript 2025 you can make an iterable from any iterator, just by calling Iterator.from
, demonstrated in this corrected script:
function iterateThis(arr){
let i = 0;
return {
next() {
return i < arr.length ?
{value: arr[i++], done: false} :
{done: true};
}
};
}
const iterable = iterateThis([1,2,3,4,5]);
for (const item of Iterator.from(iterable)) {
console.log(item);
}
Upvotes: 0
Reputation: 32226
The problem is that your iterateThis
function returns an iterator but the for/of
construct expects a iterable.
Okay, wait, whats the difference?
From MDN's page on iteration protocols:
In order to be iterable, an object must implement the
@@iterator
method, meaning that the object (or one of the objects up its prototype chain) must have a property with a@@iterator
key which is available via constantSymbol.iterator
:
On the other hand:
An object is an iterator when it implements a
next()
method with the following semantics: Ommited due to length, TL;DR: The next method returns an object of the form:{value: T, done: boolean}
They are related in that calling the @@iterator
method of an iterable returns an iterator.
The for/of
loop always expects an iterable, so if you want to use for/of
, you have to use @@iterator
/Symbol.iterator
. There's just no way around it as far as I know. But your snippet can be easily modified to use it by just creating an object that returns your iterator when it's Symbol.iterator
method is called:
function iterateThis(arr){
let i = 0;
return {
next: function() {
return i < arr.length ?
{value: arr[i++], done: false} :
{done: true};
}
};
}
function makeIterableFromIterator(iterator) {
return {
[Symbol.iterator]: function() {
return iterator;
}
}
}
const iterator = iterateThis([1, 2, 3, 4, 5]);
const iterable = makeIterableFromIterator(iterator);
for (item of iterable) {
console.log(item);
}
Upvotes: 5