Starlyvil
Starlyvil

Reputation: 93

How to make custom iterator interface iterable in JavaScript

I have an array, and i have overridden its default Iterator behavior. The problem is that, after overriding, the returned iterator becomes non iterable and for..of..loop fails on it but works on the array, but explicit call on the array's iterator next() method still works. Here is the code below:

let arr = ["A", "B", "C", "D", "E", "F"];
arr[Symbol.iterator] = function(){
  let i = 0;

  return {
    //Iterator interface
    next:function(){
      //IteratorResult Interface
      return {
        value: arr[i++]+"..",
        done: arr[i] == undefined?true:false
      }
    }
  }
}

And here is the iterator object

let arrIterator = arr[Symbol.iterator](); //An iterator object returned but not iterable

Consumption trial with for..of..loop on iterator object

for (let i of arrIterator){
  console.log(i);
}

OUTPUT

for..of..loop trial on iterator result

Consumption trial with for..of..loop on array

for (let i of arr){
  console.log(i);
}

OUTPUT

for..of..loop trial on array result

Consumption trial with explicit next() method call

console.log(arrIterator.next());
console.log(arrIterator.next());
console.log(arrIterator.next());
console.log(arrIterator.next());
console.log(arrIterator.next());
console.log(arrIterator.next());
console.log(arrIterator.next());
console.log(arrIterator.next());

OUTPUT

explicit next() method call on iterator result

Please i really want to know how to make the custom iterator iterable for for..of..loop consumption.

Thanks

Upvotes: 2

Views: 719

Answers (2)

trincot
trincot

Reputation: 350310

As of ECMAScript 2025 we have Iterator.from, which creates an iterator from the given argument, which can be an iterable:

// Your code
let arr = ["A","B","C","D","E","F"]; arr[Symbol.iterator] = function(){ let i = 0;return {next(){return {value: arr[i++]+"..",done: arr[i] == undefined}}}}
let arrIterator = arr[Symbol.iterator]();

// call Iterator.from here
for (let i of Iterator.from(arrIterator)) {
    console.log(i);
}

This Iterator.from method returns a proper iterator, that inherits from Iterator, and includes a return method.

Upvotes: 1

Gabriele Petrioli
Gabriele Petrioli

Reputation: 196002

You need to implement the Iterable protocol as well (instead of just the Iterator)

Add [Symbol.iterator]: function() { return this; } to your Iterator object and it will work.

let arr = ["A", "B", "C", "D", "E", "F"];
arr[Symbol.iterator] = function() {
  let i = 0;

  return {
    //Iterator interface
    next: function() {
      //IteratorResult Interface
      return {
        value: arr[i++] + "..",
        done: arr[i] == undefined ? true : false
      }
    },
    // Iterable interface
    [Symbol.iterator]: function() {
      return this;
    }
  }
}

let arrIterator = arr[Symbol.iterator](); //An iterator object returned but not iterable

for (let i of arrIterator) {
  console.log(i);
}

See Iteration protocols for more info

Upvotes: 2

Related Questions