Iterator HelpersLoading saved progress…

Iterator Helpers

Iterator helpers are lazy, chainable transformations over any iterator — map, filter, take, drop, and flatMap that pull one value at a time instead of building a new array at each step. This is the model behind the ES2025 Iterator Helpers that ship on Iterator.prototype. Because nothing runs until a value is pulled, the same chain that transforms a three-item array can also run over an infinite sequence and stop the moment it has enough.

You will build iteratorHelpers, a function that wraps any iterable — an array, a Set, a generator, even a generator that never ends — and returns a helper with lazy, chainable methods plus terminal methods that actually consume.

Signature

iteratorHelpers(iterable); // -> a lazy, chainable, iterable helper

// Lazy — each returns a NEW helper and does no work until pulled:
//   .map(fn)       transform each value
//   .filter(pred)  keep values where pred is truthy
//   .take(n)       yield at most the first n values, then stop
//   .drop(n)       skip the first n values, yield the rest
//   .flatMap(fn)   fn returns an iterable per value; flatten one level

// Terminal — these pull values and produce a result:
//   .toArray()          collect every value into an array
//   .forEach(fn)        call fn on each value
//   .reduce(fn, init)   fold to a single value

// The helper is itself iterable, so you can for..of it or spread it.

Examples

iteratorHelpers([1, 2, 3, 4, 5])
  .filter((x) => x % 2 === 1)
  .map((x) => x * 10)
  .toArray();
// [10, 30, 50]
// Works on an INFINITE source, because take stops pulling after 3:
function* naturals() {
  let i = 1;
  while (true) yield i++;
}

iteratorHelpers(naturals())
  .map((x) => x * x)
  .take(3)
  .toArray();
// [1, 4, 9]   (never tries to read the whole sequence)

Notes

  • Lazy means nothing runs until pulled — building a chain of .map().filter().take() does no work. Values flow one at a time only when a terminal method or for..of asks for them.
  • take(n) must stop the source — on an infinite iterator, take(3) has to yield three values and then pull no more, or the program hangs. This is the crux of the question.
  • Do not materialize between steps — each operator forwards one value at a time; do not build an intermediate array (that would break infinite sources and do wasted work).
  • flatMap flattens one levelfn returns an iterable per value; you flatten those by one level, not recursively.
  • Single-use — a helper wraps one underlying iterator; once you consume it (with a terminal or for..of), it is exhausted, just like a native iterator.
  • Out of scope — no async iterators and no Symbol.asyncIterator; everything here is synchronous and pull-based.

FAQ

What makes iterator helpers lazy?
Each of map, filter, take, drop, and flatMap returns a generator that pulls one value from its upstream only when asked. Building a chain does no work; values flow only when a terminal method or for..of pulls them.
Why can take work on an infinite sequence?
take(n) stops pulling from its source after the nth value, so a chain like naturals().map(f).take(3) reads exactly what it needs and terminates. An eager array-based version would try to read every value and hang.
How is this different from Array.prototype.map and filter?
Array methods are eager: they materialize a whole new array at each step and visit every element. Iterator helpers are lazy and pull-based, so map(fn).take(2) calls fn only twice.
Loading editor…