Iterator.prototype.filter()
Yield only the values of an iterator for which a callback returns truthy, lazily and on demand.
filter(callbackFn)- Added in JavaScript ES2025 (Baseline March 2025)
Parameters
| Parameter | Type | Description |
|---|---|---|
$callbackFn | Function | Called for each element as callbackFn(element, index). Return a truthy value to keep the element or a falsy value to skip it. element is the current value and index is its zero-based position in the source iteration. |
Return value
Iterator HelperA new iterator helper object that yields only those elements for which callbackFn returned a truthy value; when the underlying iterator is completed it produces { value: undefined, done: true }.
Errors
Throws a TypeError if callbackFn is not callable, or if the method is called on a value that is not an object.
Filtering an iterator costs nothing until it is consumed. Each next() on the helper pulls values from the source and discards them until one passes the test, then yields that one. Rejected values are never buffered, so memory stays flat no matter how selective the predicate is.
When to reach for it
- Narrowing a generator or a
Map/Setiterator before doing anything expensive with the survivors. - Finding the first few matches in a long sequence:
src.filter(pred).take(3)stops as soon as three are found. - Any pipeline where
array.filter()would build an intermediate array you immediately throw away.
What trips people up
The index passed to the callback counts positions in the source, not in the output. Two elements that both pass will not have consecutive indices unless everything between them passed too.
The bigger trap is unbounded filtering. If nothing in an infinite source ever satisfies the predicate, the first next() spins forever — the helper has no way to know the match will never come. Guard endless sources with take() upstream when the predicate might match nothing.
Example
When to use it
- Pull the first few even numbers out of an unbounded number generator
- Keep only in-stock entries from a Map before formatting them
- Drop malformed records from a parsed stream without allocating an intermediate array
Frequently asked questions
Does filter() on an iterator return an array?
No, it returns a lazy iterator helper. Call toArray() at the end of the chain if you need an array, or iterate it directly with for...of. This differs from Array.prototype.filter(), which returns a new array immediately.
Why does filter() hang on my infinite generator?
Because a predicate that never matches gives the helper nothing to yield, so next() keeps pulling forever. The helper cannot know no match is coming. Bound the source with take() before filtering, or make sure the predicate is satisfied infinitely often.
Which is faster, array.filter().slice() or values().filter().take()?
The iterator version, when you only need the first few matches. array.filter() tests every element and builds a full array before slice() throws most of it away, whereas filter().take(n) stops testing as soon as n elements have passed. For short arrays consumed in full, the difference is negligible.
More examples
Index counts the source, not the result
The logged indices run 0,1,2,3 across every element examined, including the rejected "a" at index 0 — the kept values do not get renumbered 0,1,2.
const words = ["a", "bbb", "cc", "dddd"].values();
const long = words.filter((w, i) => {
console.log("testing", w, "at index", i);
return w.length >= 2;
});
console.log(long.toArray()); // ["bbb", "cc", "dddd"]Filtering Map entries before formatting
Map.prototype.entries() returns an iterator, so the helpers chain directly onto it. The out-of-stock "pear" never reaches the map() callback, and no intermediate array is created between the two steps.
const stock = new Map([
["apple", 3],
["pear", 0],
["fig", 7],
]);
const available = stock
.entries()
.filter(([, count]) => count > 0)
.map(([name, count]) => `${name}: ${count}`)
.toArray();
console.log(available); // ["apple: 3", "fig: 7"]
Discussion