web-dev-qa-db-fra.com

Comment faire un itérateur d'une classe ES6

Comment créer un itérateur à partir d'une classe ES6 de la même manière que la syntaxe JS1.7 SomeClass.prototype.__iterator__ = function() {...}?

[EDIT 16:00]

Les oeuvres suivantes:

class SomeClass {
    constructor() {
    }

    *[Symbol.iterator]() {
        yield '1';
        yield '2';
    }

    //*generator() {
    //}

}

an_instance = new SomeClass();
for (let v of an_instance) {
    console.log(v);
}

WebStorm marque *[Symbol.iterator]() avec l'avertissement 'nom de fonction attendu' directement après l'astérisque, mais sinon, cela se compile et fonctionne correctement avec Traceur. (Remarque: WebStorm ne génère aucune erreur pour *generator().)

50
user5321531

Définissez une méthode d'itérateur appropriée. Par exemple:

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

Edit: Exemple d'utilisation:

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

Vous devez spécifier la propriété Symbol.iterator pour SomeClass qui renvoie iterator pour les instances de classe. Iterator doit avoir la méthode next(), qui à son tour renvoie un objet avec les champs done et value. Exemple simplifié:

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) }
    }
  };
};

Ou en utilisant les classes ES6 et les fonctions de flèche:

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) })
    };
  };
}

Et utilisation:

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

Dans votre question mise à jour, vous avez réalisé les fonctions class iterator à generator. Vous pouvez le faire, mais vous devez comprendre que l’itérateur ne peut pas être un générateur. En fait, iterator dans es6 est un objet qui a une méthode spécifique next()

27
alexpods

Voici un exemple d'itération sur une classe personnalisée de matrice 2d dans 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;
            }
        }
    }
}

L’utilisation d’une telle classe serait

let matrix = new Matrix();

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

Quel serait sortie

1
2
9
5
3
8
4
6
7
18
Shaheen Ghiassy

Documentation: Protocoles d'itération

Exemple de classe implémentant les techniques iterator protocol et iterable protocol:

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();
  }
}

Accéder aux éléments en utilisant l'une ou l'autre 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);
9
ekillaby

Exemple de classe d'itérateur ES6 stockée dans un sous-objet:

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;
    }
}
0
kyleo347

Explication

Faire un objet iterable signifie que cet objet a une méthode nommée avec le Symbol.iterator. Lorsque cette méthode est appelée, elle devrait renvoyer une interface appelée iterator .

Ce itérateur doit avoir une méthode next qui retourne le résultat suivant. Ce résultat doit être un objet avec une propriété value qui fournit la valeur suivante et une propriété done, qui devrait être true lorsqu'il n'y a plus de résultats et false sinon.

La mise en oeuvre

Je vais également implémenter un itérateur pour une classe appelée Matrix dont tous les éléments seront compris entre 0 et width * height - 1. Je vais créer une classe différente pour cet itérateur appelé 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};
    }
}

Notez que Matrix implémente le protocole iterator en définissant le symbole Symbol.iterator. Dans cette méthode, une instance de MatrixIterator est créée. Elle prend this, c'est-à-dire l'instance Matrix en tant que paramètre, et dans MatrixIterator, la méthode next est définie. J'aime particulièrement cette façon de mettre en œuvre un itérateur car elle montre clairement le itérateur et la mise en œuvre du Symbol.iterator.

Sinon, on ne peut pas non plus définir directement Symbol.iterator, mais ajouter une fonction à prototype[Symbol.iterator] comme suit:

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

Exemple d'utilisation

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