anonymouse
anonymouse

Reputation: 639

Adding a custom iterator to a javascript class

I'm trying to figure out how to add an itertor to a javascript class such that the class could be used in a for...in loop. Following the instructions from Mozilla doesn't produce the results they claim it will. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators Jsfiddle of given example: http://jsfiddle.net/KQayW/

function Range(low, high){
  this.low = low;
  this.high = high;
  this.current = low;
  this.next = function() {
    if (this.current > this.range.high)
      throw StopIteration;
    else
      return this.current++;
  }
}
function RangeIterator(range){
  this.range = range;
  this.current = this.range.low;
}
RangeIterator.prototype.next = function(){
  if (this.current > this.range.high)
    throw StopIteration;
  else
    return this.current++;
};
Range.prototype.__iterator__ = function(){
  return new RangeIterator(this);
};
var range = new Range(3, 5);
for (var i in range)
  document.getElementById("test").innerHTML = i+"</br>"; // prints 3, then 4, then 5 in sequence

It doesn't print out the numbers in the range, it prints out "__iterator__"!

Does anyone know how to get this to work?

Upvotes: 11

Views: 8342

Answers (5)

trincot
trincot

Reputation: 350310

With ES2025 we can chain iterator helper methods:

const Range = (start, end) => Array(end - start).keys().map(x => x + start)

for (const x of Range(1, 10)) {
    console.log(x)
}

Although an Array instance is created, it is one that remains "empty", i.e. it doesn't allocate index slots and so it takes constant space, irrespective of the argument given. The keys method returns an iterator, and map is an iterator method (not an array method) that returns the desired iterator.

Upvotes: 0

James
James

Reputation: 6113

Or you can do it without the generator syntax. This may help if you are trying to understand how iterators work.

function range(start, end) {
    return {
        [Symbol.iterator]() {
            return this;
        },
        next() {
            return start <= end ? {value: start++} : {done: true};
        }
    };
}

console.log(...range(1, 10));

The range() function returns an 'iterable object'. The iterable object is iterable because it has a function [Symbol.iterator]() that returns an 'iterator object'. The iterator object is an iterator because it has a next() function.

In this case the iterable and the iterator are the same object, which simplifies it.

Upvotes: 2

Krzysztof Safjanowski
Krzysztof Safjanowski

Reputation: 7438

With ES2015 it can be even more simple

const Range = (start, end) => ({
  *[Symbol.iterator]() {
    while (start < end) 
        yield start++;
  }
})

for (var x of Range(1, 10)) {
  console.log(x)
}

Upvotes: 10

marc meyer
marc meyer

Reputation: 580

With ES2015 its simple:

function Range(start, end) {
    var ret = {};
    ret[Symbol.iterator] = function *() {
        while (start < end) 
            yield start++;
    }
    return ret;
}

Though you have to use:

for (var x of Range(1, 10))

Upvotes: 9

Johnathan Hok
Johnathan Hok

Reputation: 99

The Mozilla document state that the Iterators feature was introduced in JavaScript 1.7. Although Chrome supports some features from 1.7, it isn't fully supported so this does not work. If you test your code in the latest Firefox version though you will see it works.

Although you probably want to append the range value rather than replacing the entire div.

http://jsfiddle.net/KQayW/2/

function Range(low, high){
  this.low = low;
  this.high = high;
  this.current = low;
  this.next = function() {
  if (this.current > this.range.high)
    throw StopIteration;
  else
    return this.current++;
  }
}
function RangeIterator(range){
  this.range = range;
  this.current = this.range.low;
}
RangeIterator.prototype.next = function(){
  if (this.current > this.range.high)
    throw StopIteration;
  else
    return this.current++;
};
Range.prototype.__iterator__ = function(){
  return new RangeIterator(this);
};
var range = new Range(3, 5);
for (var i in range)
  document.getElementById("test").innerHTML += i+"</br>"; // prints 3, then 4, then 5 in sequence

Upvotes: 3

Related Questions