How to make an iterator out of an ES6 class

天大地大妈咪最大 提交于 2019-12-02 16:29:58

Define a suitable iterator method. For example:

class C {
  constructor() { this.a = [] }
  add(x) { this.a.push(x) }
  [Symbol.iterator]() { return this.a.values() }
}

Edit: Sample use:

let c = new C
c.add(1); c.add(2)
for (let i of c) console.log(i)
alexpods

You need to specify Symbol.iterator property for SomeClass which returns iterator for class instances. Iterator must have next() method, witch in turn returns object with done and value fields. Simplified example:

function SomeClass() {
  this._data = [1,2,3,4];
}

SomeClass.prototype[Symbol.iterator] = function() {
  var index = 0;
  var data  = this._data;

  return {
    next: function() {
      return { value: data[++index], done: !(index in data) }
    }
  };
};

Or using ES6 classes and arrow functions:

class SomeClass {
  constructor() {
    this._data = [1,2,3,4];
  }

  [Symbol.iterator]() {
    var index = -1;
    var data  = this._data;

    return {
      next: () => ({ value: data[++index], done: !(index in data) })
    };
  };
}

And usage:

var obj = new SomeClass();
for (var i of obj) { console.log(i) }

In your updated question you realized class iterator through generator function. You can do so, but you must understand that iterator COULD NOT BE a generator. Actually iterator in es6 is any object that has specific next() method

Here's an example for iterating over a 2d matrix custom class in ES6

class Matrix {
    constructor() {
        this.matrix = [[1, 2, 9],
                       [5, 3, 8],
                       [4, 6, 7]];
    }

    *[Symbol.iterator]() {
        for (let row of this.matrix) {
            for (let cell of row) {
                yield cell;
            }
        }
    }
}

The usage of such a class would be

let matrix = new Matrix();

for (let cell of matrix) {
    console.log(cell)
}

Which would output

1
2
9
5
3
8
4
6
7

Documentation: Iteration Protocols

Example class implementing both iterator protocol and iterable protocol techniques:

class MyCollection {
  constructor(elements) {
    if (!Array.isArray(elements))
      throw new Error('Parameter to constructor must be array');

    this.elements = elements;
  }

  // Implement "iterator protocol"
  *iterator() {
    for (let key in this.elements) {
      var value = this.elements[key];
      yield value;
    }
  }

  // Implement "iterable protocol"
  [Symbol.iterator]() {
    return this.iterator();
  }
}

Access elements using either technique:

var myCollection = new MyCollection(['foo', 'bar', 'bah', 'bat']);

// Access elements of the collection using iterable
for (let element of myCollection)
  console.log('element via "iterable": ' + element);

// Access elements of the collection using iterator
var iterator = myCollection.iterator();
while (element = iterator.next().value)
  console.log('element via "iterator": ' + element);

Explanation

Making an object iterable means this object has a method named with the Symbol.iterator. When this method gets called, it should return an interface called iterator.

This iterator must have a method next that returns the next result. This result should be an object with a value property that provides the next value, and a done property, which should be true when there are no more results and false otherwise.

Implementation

I will also implement an iterator for a class called Matrix which all elements will range from 0 to width * height - 1. I will create a different class for this iterator called MatrixIterator.

class Matrix {
    constructor(width, height) {
        this.width = width;
        this.height = height;
        this.content = [];

        for (let y = 0; y < height; y++) {
            for (let x = 0; x < width; x++) {
                this.content[y * width + x] = y * width + x;
            }
        }
    }

    get(x, y) {
        return this.content[y * this.width + x];
    }

    [Symbol.iterator]() {
        return new MatrixIterator(this);
    }
}


class MatrixIterator {
    constructor(matrix) {
        this.x = 0;
        this.y = 0;
        this.matrix = matrix;
    }

    next() {
        if (this.y == this.matrix.height) return {done: true};

        let value = {
            x: this.x,
            y: this.y,
            value: this.matrix.get(this.x, this.y)
        };

        this.x++;

        if (this.x == this.matrix.width) {
            this.x = 0;
            this.y++;
        }

        return {value, done: false};
    }
}

Notice that Matrix implements the iterator protocol by defining Symbol.iterator symbol. Inside this method, an instance of MatrixIterator is created which takes this, i.e., the Matrix instance as parameter, and inside MatrixIterator, the method next is defined. I particularly like this way of implementing an iterator because it clearly shows the iterator and the implementation of the Symbol.iterator.

Alternatively, one can also not define directly Symbol.iterator, and instead add a function to prototype[Symbol.iterator] as follows:

Matrix.prototype[Symbol.iterator] = function() {
    return new MatrixIterator(this);
};

Usage Example

let matrix = new Matrix(3, 2);
for (let e of matrix) {
    console.log(e);
}
kyleo347

Example of an ES6 iterator class that stores in a sub-object:

class Iterator {
    data;

    constructor(data = {}) {
        this.data = JSON.parse(JSON.stringify(data));
    }

    add(key, value) { this.data[key] = value; }

    get(key) { return this.data[key]; }

    [Symbol.iterator]() {
        const keys = Object.keys(this.data).filter(key => 
        this.data.hasOwnProperty(key));
        const values = keys.map(key => this.data[key]).values();
        return values;
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!