イテレーター・反復可能オブジェクト・ジェネレーター

目次

イテレーター(iterator)

イテレーターとは

イテレーター(iterator) は繰り返し処理を実現するための下記特徴を持つオブジェクトです。

イテレーターの実装例

下記がイテレーターの実装例です。

JavaScript
class MyRangeIterator {
  constructor(n, m) {
    this.n = n;
    this.m = m;
  }
  next() {
    return { value: this.n, done: this.n++ > this.m };
  }
}

反復可能オブジェクト(iterable object)

反復可能オブジェクトとは

反復可能オブジェクト(iterable object) は下記の特徴を持つオブジェクトです。

反復可能オブジェクトの実装例

JavaScript
class MyRange {
  constructor(n, m) {
    this.n = n;
    this.m = m;
  }
  [Symbol.iterator]() {
    return new MyRangeIterator(this.n, this.m);
  }
}

for (var n of new MyRange(1, 5)) {
  console.log(n);      // 1, 2, 3, 4, 5
}

下記の様にイテレーター自体を反復可能オブジェクトにすることもできます。

JavaScript
class MyRange {
  constructor(n, m) {
    this.n = n;
    this.m = m;
  }
  next() {
    return { value: this.n, done: this.n++ > this.m };
  }
  [Symbol.iterator]() {
    return this;
  }
}

for (var n of new MyRange(1, 5)) {
  console.log(n);          // => 1, 2, 3, 4, 5
}

ジェネレーター(generator)

ジェネレーターとは

ジェネレーター(generator)は反復可能オブジェクトを簡単に生成できる仕組みです。

ジェネレーターの実装例

ここでは myRange() というジェネレーター関数を定義しています。ジェネレーター関数はジェネレーターを返却します。ジェネレーターはイテレーターでもあり、反復可能オブジェクトでもあるオブジェクトです。

JavaScript
function* myRange(n, m) {
  while (n <= m) {
    yield n++;
  }
}

for (var n of myRange(1, 5)) {
  console.log(n);          // => 1, 2, 3, 4, 5
}

yield * に反復可能オブジェクトを指定するとその値を順番に返却してくれます。下記の例では yield* で反復可能オブジェクトである Array() オブジェクトを指定しています。

JavaScript
function* myRange(n, m) {
  let array = new Array();
  for (let i = n; i <= m; i++) {
    array.push(i);
  }
  yield* array;
}

for (var n of myRange(1, 5)) {
  console.log(n);          // => 1, 2, 3, 4, 5
}

ヘルパーメソッド

ES2025 で反復可能オブジェクトに下記のメソッドがサポートされました。

iterableObject.map(function)

それぞれの要素に関数を実行した反復可能オブジェクトを返します。

for (var n of myRange(1, 5).map(value => value * 2)) {
  console.log(n);         // 2, 4, 6, 8, 10
}

iterableObject.filter(function)

関数の結果が true となる要素からなる反復可能オブジェクトを返します。

for (var n of myRange(1, 5).filter(value => value % 2 == 0)) {
  console.log(n);         // 2, 4
}

iterableObject.take(n)

最初の n 個のみを抽出した反復可能オブジェクトを返します。

for (var n of myRange(1, 5).take(3)) {
  console.log(n);         // 1, 2, 3
}

iterableObject.drop(n)

最初の n 個を読み飛ばした反復可能オブジェクトを返します。

for (var n of myRange(1, 5).drop(3)) {
  console.log(n);         // 4, 5
}

iterableObject.flatMap(function)

それぞれの要素に関数を実行した結果をフラットにした反復可能オブジェクトを返します。複数要素からなる配列を返却すると、要素の数が増えます。下記の例では 1 から 1 と 1.5、2 から 2 と 2.5, ... を生成しています。

for (var n of myRange(1, 5).flatMap(value => [value, value + 0.5])) {
  console.log(n);         // 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5
}

iterableObject.reduce(function)

それぞれの要素に対して逐次的に関数を実行し、その最終結果を返します。下記の例で sum は前回の関数の戻り値、value は今回の要素を示します。下記の様にして 1 から 5 の合計 15 を求めることができます。

console.log(
  myRange(1, 5).reduce((sum, value) => sum + value)  // 15
);

iterableObject.toArray()

反復可能オブジェクトを配列に変換します。

console.log(
  myRange(1, 5).toArray()     // [1, 2, 3, 4, 5]
);

iterableObject.forEach(function)

反復可能オブジェクトに対してループ処理を行います。

myRange(1, 5).forEach((n) => {
  console.log(n)          // 1, 2, 3, 4, 5
});

iterableObject.some(function)

それぞれの要素に対して関数を実行し、結果がひとつでも true であれば true を返します。下記の例では 1 から 5 の中にひとつでも 3 と等しいものがあれば true を返します。

console.log(
  myRange(1, 5).some(v => v == 3)    // true
);

iterableObject.every(function)

それぞれの要素に対して関数を実行し、結果がすべて true であれば true を返します。下記の例では 1 から 5 の要素がすべて 10 未満であれば true を返します。

console.log(
  myRange(1, 5).every(v => v < 10)   // true
);

iterableObject.find(function)

それぞれの要素に対して関数を実行し、結果が true であった要素の個数を返します。下記の例では 1 から 5 の要素の内、3 以上である要素の個数を返します。

console.log(
  myRange(1, 5).find(v => v >= 3)    // 3
);

Iterator.from(iterator)

イテレーターを反復可能オブジェクトに変換します。

class MyRangeIterator {
  constructor(n, m) {
    this.n = n;
    this.m = m;
  }
  next() {
    return { value: this.n, done: this.n++ > this.m };
  }
}
const iterator = new MyRangeIterator(1, 5);         // イテレーター
const iterable_object = Iterator.from(iterator);    // 反復可能オブジェクト
for (var n of iterable_object) {
  console.log(n);
}