Skip to content
back to blog
developmentJuly 23, 2026 · 4 min read

JavaScript Iterator Helpers: Stop Converting to Arrays First

Iterator helpers bring map, filter, take, and drop to any iterator without spreading into an array first. They went Baseline in 2025, and they change how you handle Maps, Sets, and generators.

Dan Holloran
Dan Holloran
Senior Frontend & Fullstack Developer
JavaScript Iterator Helpers: Stop Converting to Arrays First

You have a Set of user IDs, or a Map of cache entries, or a generator streaming rows out of a parser. You want the first ten that match a condition. So you reach for the move you have made a thousand times: spread it into an array, call .filter(), then .slice(0, 10).

It works. It also builds the entire array in memory just to keep ten items. For a Set of fifty entries nobody notices. For a generator yielding a million rows, you just materialized the whole stream to throw away most of it. That whole [...thing] dance exists only because map and filter used to live exclusively on Array.prototype. As of 2025, they do not.

The array methods you know, now on iterators

Iterator helpers add a batch of familiar methods directly to Iterator.prototype: map, filter, take, drop, and flatMap return new iterators, while reduce, toArray, forEach, some, every, and find consume the iterator and hand back a value. Because they live on the prototype, anything whose prototype chain includes Iterator.prototype gets them for free, including the iterators returned by array.values(), map.entries(), and set.values().

So the opening example loses the spread entirely:

js
const activeAdmins = users
  .values()
  .filter((u) => u.role === "admin")
  .filter((u) => u.active)
  .take(10)
  .toArray();

users here can be a Set, a Map, or an array. You call .values() to get an iterator, chain the helpers, and only call .toArray() at the very end when you actually want a concrete array. Everything before that stays an iterator.

Laziness is the whole point

The helpers are lazy. They do not compute anything until something pulls a value out, and they only pull as many values as they need. That is a real behavioral difference from array methods, not a micro-optimization.

Array methods are eager. array.filter(fn).slice(0, 10) runs fn against every element, builds a full intermediate array, and then slices it. The iterator version runs fn only until take(10) has its ten items, then stops. On a big collection that is the difference between touching every element and touching just enough of them.

The clearest demonstration is an infinite generator, which array methods cannot handle at all:

js
function* naturals() {
  let n = 1;
  while (true) yield n++;
}

const firstFiveSquares = naturals()
  .map((n) => n * n)
  .take(5)
  .toArray();
// [1, 4, 9, 16, 25]

Try that with [...naturals()] and the tab hangs forever. With take(5) sitting downstream, the generator is asked for exactly five values and then never again. Ordering matters here: put take before an expensive map when you can, so the map only runs on the values that survive.

Iterator.from wraps anything iterable

Not every source is already a real Iterator. Some older APIs and hand-rolled objects implement Symbol.iterator but do not inherit the helper methods. Iterator.from() normalizes them:

js
function* readLines(file) {
  /* yields one line at a time */
}

const errorCount = Iterator.from(readLines("app.log"))
  .filter((line) => line.includes("ERROR"))
  .reduce((count) => count + 1, 0);

Iterator.from returns a real iterator you can chain against. If you pass it something that is already an iterator, you get it straight back, so it is safe to wrap defensively.

Where this actually lands

Iterator helpers reached Baseline "newly available" on March 31, 2025, meaning current Chrome, Firefox, and Safari all ship them, alongside Node 22 LTS and newer, Deno 2, and Bun. If you support older runtimes, core-js polyfills the full set, so you can adopt the syntax today and let the polyfill fill the gaps.

The mental shift is small but worth making: stop spreading collections into arrays out of habit. When you are working with a Map, a Set, or a generator and you only need part of it, chain the helpers off .values() and call .toArray() last, if at all. Your code reads the same and stops doing work the moment it has what it asked for. The MDN Iterator reference has the full method list when you want to go deeper.

~/subscribe
# new posts on code, craft & travel — no noise, no schedule
$subscribe