Skip to content

Iterators

Eugene Lazutkin edited this page Aug 15, 2024 · 6 revisions

JavaScript provides a notion of iterators and helpers to deal with them:

The Iterator is the most interesting one for us, but it is mostly an experimental feature. This module doesn't try to reimplement it, but it provides a set of helpers to simplify some common tasks with iterators. Over time when various vendors implement their own iterators, we will switch to use Iterator.

iterators.js

Legend for tables

The following utilities are available:

Function Return value Description
augmentIterator(iterator) iterator Augment iterator with an iterable interface if it is not defined.
normalizeIterator(iterator) iterator Similar to augmentIterator(), but can use Iterator.from() if it is defined.
mapIterator(iterator, callbackFn) iterator Creates an iterator that calls the callback function for each element and returns the result as a value.
filterIterator(iterator, callbackFn) iterator Creates an iterator that calls the callback function for each element and returns its value if the callback function returns truthy.

augmentIterator() and normalizeIterator() are here to bridge the gap between the iterable protocol and the iterator protocol to connect to all JavaScript iteration mechanisms.

The iterator protocol defines a set of methods that can be used to iterate over a collection of elements. The iterable protocol defines a special method Symbol.iterator that returns an iterator. for...of and other JavaScript facilities expects iterables. The way to make an iterable out of an iterator is to define Symbol.iterator on the iterator returning this. That what augmentIterator() does.

normalizeIterator() try to use Iterator.from() if is available and resorts to augmentIterator().

In most cases, augmentIterator() and normalizeIterator() are used by library implementors. In the future, we can rely solely on Iterator.from(), but for now normalizeIterator() can be used instead.

mapIterator() and filterIterator() are helpers modelled on Iterator.prototype.map() and Iterator.prototype.filter() respectively.

Examples

import {normalizeIterator, filterIterator, mapIterator} from './iterators.js';

class Range {
  constructor(from, to) { this.from = from; this.to = to; }

  [Symbol.iterator]() {
    let index = this.from;
    return normalizeIterator({
      next: () => {
        if (index >= this.to) return {done: true};
        return {value: index++, done: false};
      }
    });
  }

  static from(from, to) { return new Range(from, to); }
}

for (const x of Range.from(1, 5)) {
  console.log(x); // 1, 2, 3, 4, 5
}

const range = new Range(1, 5),
  odds = filterIterator(range, x => x % 2),
  squared = mapIterator(odds, x => x * x);

for (const x of squared) {
  console.log(x); // 1, 9, 25
}

Exports

All functions are exported by their names. There is no default export.

Clone this wiki locally