Synchronous iteration is a protocol (interfaces plus rules for using them) that connects two groups of entities in JavaScript:
Data sources: On one hand, data comes in all shapes and sizes. In JavaScript’s standard library, we have the linear data structure Array, the ordered collection Set (elements are ordered by time of addition), the ordered dictionary Map (entries are ordered by time of addition), and more. In libraries, we may find tree-shaped data structures and more.
Data consumers: On the other hand, we have a whole class of constructs and algorithms that only need to access their input sequentially: one value at a time, until all values were visited. Examples include the for-of
loop and spreading into Array literals (via ...
).
The iteration protocol connects these two groups via the interface Iterable
: data sources deliver their contents sequentially “through it”; data consumers get their input from it.
Figure 32.1: Data consumers such as the for-of
loop use the interface Iterable
. Data sources such as Arrays
implement that interface.
Figure 32.1 illustrates how iteration works: data consumers use the interface Iterable
; data sources implement it.
The JavaScript way of implementing interfaces
In JavaScript, an object implements an interface if it has all the methods that it describes. The interfaces mentioned in this chapter only exist in the ECMAScript specification.
Both sources and consumers of data profit from this arrangement:
If we develop a new data structure, we only need to implement Iterable
and a raft of tools can immediately be applied to it.
If we write code that uses iteration, it automatically works with many sources of data.
Two roles (described by interfaces) form the core of iteration (figure 32.2):
Figure 32.2: Iteration has two main interfaces: Iterable
and Iterator
. The former has a method that returns the latter.
These are type definitions (in TypeScript’s notation) for the interfaces of the iteration protocol:
interface Iterable<T> {
[Symbol.iterator]() : Iterator<T>;
}
abstract class Iterator<T> {
abstract next() : IteratorResult<T>;
}
interface IteratorResult<T> {
value: T;
done: boolean;
}
The interfaces are used as follows:
Iterable
for an iterator via the method whose key is Symbol.iterator
.
Iterator
and returns the iterated values via its method .next()
.
Iterator
was simply an interface. No globally accessible class Iterator
existed.
.value
is the iterated value.
.done
indicates if the end of the iteration has been reached yet. It is true
after the last iterated value and false
beforehand.
This is an example of using the iteration protocol:
const iterable = ['a', 'b'];
// The iterable is a factory for iterators:
const iterator = iterable[Symbol.iterator]();
// Call .next() until .done is true:
assert.deepEqual(
iterator.next(), { value: 'a', done: false }
);
assert.deepEqual(
iterator.next(), { value: 'b', done: false }
);
assert.deepEqual(
iterator.next(), { value: undefined, done: true }
);
while
to iterate manuallyThe following code demonstrates how to use a while
loop to iterate over an iterable:
function logAll(iterable) {
const iterator = iterable[Symbol.iterator]();
while (true) {
const {value, done} = iterator.next();
if (done) break;
console.log(value);
}
}
logAll(['a', 'b']);
Output:
a
b
Exercise: Using sync iteration manually
exercises/sync-iteration/sync_iteration_manually_exrc.mjs
Iterator.from()
ES2024The built-in static method Iterator.from()
provides us with a more elegant way of retrieving iterators:
> const iterable = ['a', 'b'];
> iterable[Symbol.iterator]() instanceof Iterator
true
> Iterator.from(iterable) instanceof Iterator
true
We have seen how to use the iteration protocol manually and it is relatively cumbersome. But the protocol is not meant to be used directly – it is meant to be used via higher-level language constructs built on top of it. We’ll notice that we never see iterators when we do so. They are only used internally.
The most important iteration-based language construct is the for-of
loop:
const iterable = ['hello', 'beautiful', 'world'];
for (const x of iterable) {
console.log(x);
}
Output:
hello
beautiful
world
Another iteration-based construct is spreading into Array literals:
assert.deepEqual(
['BEFORE', ...iterable, 'AFTER'],
['BEFORE', 'hello', 'beautiful', 'world', 'AFTER']
);
Destructuring via Array patterns also uses iteration under the hood:
const [first, second] = iterable;
assert.equal(first, 'hello');
assert.equal(second, 'beautiful');
Sets are also iterable. Note that the iterating code is the same: It sees neither Arrays nor Sets, only iterables.
const iterable = ['hello', 'beautiful', 'world'];
for (const x of iterable) {
console.log(x);
}
assert.deepEqual(
['BEFORE', ...iterable, 'AFTER'],
['BEFORE', 'hello', 'beautiful', 'world', 'AFTER']
);
const [first, second] = iterable;
assert.equal(first, 'hello');
assert.equal(second, 'beautiful');
[...i]
and Array.from(i)
These are ways of converting iterables to Arrays:
const iterable = new Set().add('a').add('b').add('c');
assert.deepEqual(
[...iterable],
['a', 'b', 'c']
);
assert.deepEqual(
Array.from(iterable),
['a', 'b', 'c']
);
I tend to prefer Array.from()
because it’s more self-descriptive.
More information: “Converting iterables, iterators and Array-like values to Arrays” (§34.6)
We can also create an iterator and use an iterator method to create an Array. Iterator methods are explained later.
assert.deepEqual(
Iterator.from(iterable).toArray(),
['a', 'b', 'c']
);
Synchronous generator functions and methods expose their yielded values via iterators (that are also iterable) that they return:
/** Synchronous generator function */
function* createSyncIterable() {
yield 'a';
yield 'b';
yield 'c';
}
Generators produce iterables, but they can also consume them. That makes them a versatile tool for transforming iterables:
function* map(iterable, callback) {
for (const x of iterable) {
yield callback(x);
}
}
assert.deepEqual(
Array.from(
map([1, 2, 3, 4], x => x ** 2)
),
[1, 4, 9, 16]
);
function* filter(iterable, callback) {
for (const x of iterable) {
if (callback(x)) {
yield x;
}
}
}
assert.deepEqual(
Array.from(
filter([1, 2, 3, 4], x => (x % 2) === 0
)),
[2, 4]
);
More information: “Synchronous generators ES6 (advanced)” (§33)
All of the iterators created by JavaScript’s standard library have a common prototype which the ECMAScript specification calls %IteratorPrototype%
and uses internally. We can access it from JavaScript via Iterator.prototype
.
We create an Array iterator like this:
const arrayIterator = [][Symbol.iterator]();
This object has a prototype with two properties. Let’s call it ArrayIteratorPrototype
:
const ArrayIteratorPrototype = Object.getPrototypeOf(arrayIterator);
assert.deepEqual(
Reflect.ownKeys(ArrayIteratorPrototype),
[ 'next', Symbol.toStringTag ]
);
assert.equal(
ArrayIteratorPrototype[Symbol.toStringTag],
'Array Iterator'
);
The prototype of ArrayIteratorPrototype
is %IteratorPrototype%
. This object has a method whose key is Symbol.iterator
. Therefore, all built-in iterators are iterable.
const IteratorPrototype = Object.getPrototypeOf(ArrayIteratorPrototype);
assert.equal(
IteratorPrototype, Iterator.prototype
);
assert.equal(
Object.hasOwn(Iterator.prototype, Symbol.iterator),
true
);
assert.equal(
typeof Iterator.prototype[Symbol.iterator],
'function'
);
The prototype of Iterator.prototype
is Object.prototype
.
assert.equal(
Object.getPrototypeOf(Iterator.prototype) === Object.prototype,
true
);
Figure 32.3 contains a diagram for this chain of prototypes.
Figure 32.3: A chain of prototypes (from bottom to top):
[][Symbol.iterator]()
(an instance of %ArrayIterator%
)
%ArrayIteratorPrototype%
%IteratorPrototype%
Object.prototype
Roughly, a generator object is an iterator for the values yielded by a generator function genFunc()
. We create it by calling genFunc()
:
function* genFunc() {
yield 'a';
yield 'b';
}
const genObj = genFunc();
A generator object is an iterator:
assert.deepEqual(
genObj.next(),
{ value: 'a', done: false }
);
assert.equal(
genObj instanceof Iterator,
true
);
assert.equal(
Iterator.prototype.isPrototypeOf(genObj),
true
);
As we have seen, all built-in iterators are iterable:
// Array iterator
const arrayIterator = ['a', 'b'].values();
assert.equal(
// arrayIterator is a built-in iterator
arrayIterator instanceof Iterator, true
);
assert.equal(
// arrayIterator is iterable
Symbol.iterator in arrayIterator, true
);
// Generator object
function* gen() { yield 'hello' }
const genObj = gen();
assert.equal(
genObj instanceof Iterator, true
);
assert.equal(
Symbol.iterator in genObj, true
);
// Iterator returned by `Iterator` method
const iter = Iterator.from([1, 2]).map(x => x * 2);
assert.equal(
iter instanceof Iterator, true
);
assert.equal(
Symbol.iterator in iter, true
);
That has the benefit of us being able to iterate over the iterator’s values – e.g., via for-of
and Array.from()
.
Another benefit is that generators become more versatile. On one hand, we can use them to implement iterators:
class MyIterable {
/** This method must return an iterator */
* [Symbol.iterator]() {
yield 'good';
yield 'morning';
}
}
assert.deepEqual(
Array.from(new MyIterable()),
['good', 'morning']
);
On the other hand, we can use them to implement iterables:
function* createIterable() {
yield 'a';
yield 'b';
}
assert.deepEqual(
Array.from(createIterable()),
['good', 'morning']
);
If an iterator is iterable: What are the iterators it produces? It simply returns itself when asked for an iterator:
const iterator = Iterator.from(['a', 'b'])
assert.equal(
iterator[Symbol.iterator](),
iterator
);
Alas, iterable iterators mean that there are two kinds of iterables:
An iterable iterator is a one-time iterable: It always returns the same iterator (itself) when [Symbol.iterator]()
is called (iteration continues).
A normal iterable (an Array, a Set, etc.) is a many-times iterable: It always returns a fresh iterator (iteration restarts).
With a one-time iterable, each time we iterate, we remove more elements, until, eventually, no more are left:
const oneTime = ['a', 'b', 'c'].values();
for (const x of oneTime) {
assert.equal(
x, 'a'
);
break;
}
assert.deepEqual(
Array.from(oneTime),
['b', 'c']
);
assert.deepEqual(
Array.from(oneTime),
[]
);
With a many-times iterable, each iteration starts fresh:
const manyTimes = ['a', 'b', 'c'];
for (const x of manyTimes) {
assert.equal(
x, 'a'
);
break;
}
assert.deepEqual(
Array.from(manyTimes),
['a', 'b', 'c']
);
assert.deepEqual(
Array.from(manyTimes),
['a', 'b', 'c']
);
The following code is another demonstration of the difference:
const oneTime = ['a', 'b', 'c'].values();
assert.deepEqual(
[...oneTime, ...oneTime, ...oneTime],
['a', 'b', 'c']
);
const manyTimes = ['a', 'b', 'c'];
assert.deepEqual(
[...manyTimes, ...manyTimes, ...manyTimes],
['a','b','c', 'a','b','c', 'a','b','c']
);
Iterator
and iterator helper methods ES2025We have already seen that %IteratorPrototype%
is the prototype of all built-in iterators. ECMAScript 2025 introduces a class Iterator
:
Iterator.prototype
refers to %IteratorPrototype%
.
%IteratorPrototype%.constructor
refers to Iterator
.
The class provides the following functionality:
Iterator.from(iterable)
returns an iterator for iterable
. We’ll explore it in detail later.
Iterator.prototype
contains various helper methods that are inherited by iterators.
Iterator.prototype.*
methodsThe following iterator helper methods work like the Array methods with the same names:
iterator.filter(filterFn)
iterator.map(mapFn)
iterator.flatMap(mapFn)
iterator.some(fn)
iterator.every(fn)
iterator.find(fn)
iterator.reduce(reducer, initialValue?)
iterator.forEach(fn)
These helper methods are unique to iterators:
iterator.drop(limit)
limit
elements of iterator
.
iterator.take(limit)
limit
elements of iterator
.
iterator.toArray()
iterator
in an Array and returns it.
For a brief description of each method, see “Quick reference: class Iterator
ES2025” (§32.10). These are examples of the methods in action:
assert.deepEqual(
['a', 'b', 'c'].values().map(x => `=${x}=`).toArray(),
['=a=', '=b=', '=c=']
);
assert.deepEqual(
['a', 'b', 'c'].values().drop(1).toArray(),
['b', 'c']
);
assert.deepEqual(
['a', 'b', 'c'].values().take(2).toArray(),
['a', 'b']
);
The Array method arr.values()
returns an iterator over the elements of arr
.
Exercises: Working with iterators
.at()
for iterators: exercises/sync-iteration/iterator-at_test.mjs
.findIndex()
for iterators: exercises/sync-iteration/iterator-find-index_test.mjs
.slice()
for iterators: exercises/sync-iteration/slice-iterator_test.mjs
exercises/sync-iteration/add-index-to-iterator_test.mjs
iterator.reduce()
to compute the length of an iterator: exercises/sync-iteration/iterator-length_test.mjs
With iterator helper methods, any data structure that supports iteration automatically gains functionality.
For example, Sets don’t support the operations filter
and map
, but we can get them via iterator methods:
assert.deepEqual(
new Set( // (A)
new Set([-5, 2, 6, -3]).values().filter(x => x >= 0)
),
new Set([2, 6])
);
assert.deepEqual(
new Set( // (B)
new Set([-5, 2, 6, -3]).values().map(x => x / 2)
),
new Set([-2.5, 1, 3, -1.5])
);
Note that new Set()
accepts iterables and therefore iterable iterators (line A and line B).
DOM collections also don’t have the methods .filter()
and .map()
:
const domCollection = document.querySelectorAll('a');
// Alas, the collection doesn’t have a method .map()
assert.equal('map' in domCollection, false);
// Solution: use an iterator
assert.deepEqual(
domCollection.values().map(x => x.href).toArray(),
['https://2ality.com', 'https://exploringjs.com']
);
Exercise: Implementing
.filter()
and .map()
for Sets via iterator methods
exercises/sync-iteration/set-operations-via-iterators_test.mjs
If we chain operations that return Arrays (line A, line B, line C) then each operation produces a new Array:
function quoteNonEmptyLinesArray(str) {
return str
.split(/(?<=\r?\n)/) // (A)
.filter(line => line.trim().length > 0) // (B)
.map(line => '> ' + line) // (C)
;
}
The regular expression in line A contains a lookbehind assertion which ensures that the lines returned by .split()
includes line terminators.
In contrast, each operation (line A, line B, line C) in the following code returns an iterator and no intermediate Arrays are created:
function quoteNonEmptyLinesIter(str) {
return splitLinesIter(str) // (A)
.filter(line => line.trim().length > 0) // (B)
.map(line => '> ' + line) // (C)
;
}
function* splitLinesIter(str) {
let prevIndex = 0;
while (true) {
const eolIndex = str.indexOf('\n', prevIndex);
if (eolIndex < 0) break;
// Including EOL
const line = str.slice(prevIndex, eolIndex + 1);
yield line;
prevIndex = eolIndex + 1;
}
if (prevIndex < str.length) {
yield str.slice(prevIndex);
}
}
Example of using quoteNonEmptyLinesIter()
:
assert.deepEqual(
Array.from(quoteNonEmptyLinesIter('have\n\na nice\n\nday')),
[
'> have\n',
'> a nice\n',
'> day',
]
);
Note that the empty lines between the three lines of text were filtered out.
In addition to no intermediate Arrays being created, iterators also give us incremental processing:
> const iter = quoteNonEmptyLinesIter('have\n\na nice\n\nday');
> iter.next()
{ value: '> have\n', done: false }
> iter.next()
{ value: '> a nice\n', done: false }
> iter.next()
{ value: '> day', done: false }
> iter.next()
{ value: undefined, done: true }
In contrast, quoteNonEmptyLinesArray()
first splits all lines, then filters all lines and then maps all lines. Incremental processing matters when dealing with a large amount of data. Iterator helper methods complement generators as tools for incremental processing.
Iterator.from()
: creating API iteratorsAll built-in iterables automatically support the new API because their iterators already have Iterator.prototype
as a prototype (and are therefore instances of Iterator
). However, that’s not the case for all iterables in libraries and user code.
Terminology:
Iterator
API: API iterators and API iterables
How does Iterator.from(obj)
work?
obj
is iterable, then it creates an iterator by calling obj[Symbol.iterator]()
.
Iterator
, it is returned as is.
Iterator
.
obj
is an iterator, Iterator.from()
ensures that it is an instance of Iterator
and returns it.
In the following example, we use Iterator.from()
to convert a legacy iterator to an API iterator:
// Not an instance of `Iterator`
const legacyIterator = {
next() {
// Infinite iterator (never done)
return { done: false, value: '#' };
}
};
assert.equal(
Iterator.from(legacyIterator) instanceof Iterator,
true
);
assert.deepEqual(
Iterator.from(legacyIterator).take(3).toArray(),
['#', '#', '#']
);
The iterator methods change how we use iteration:
for-of
or Array.from()
.
It’s interesting how our focus shifts with methods such as Array.prototype.keys()
that return iterable iterators: Before iterator methods, we used the result as an iterable. With iterator methods, we also use them as iterators:
> Array.from(['a', 'b'].keys()) // iterable
[ 0, 1 ]
> ['a', 'b'].keys().toArray() // iterator
[ 0, 1 ]
For more information, see “Creating iterators” (§32.10.1).
Iterator
APIThis is an example of a legacy iterable:
class ValueIterable {
#values;
#index = 0;
constructor(...values) {
this.#values = values;
}
[Symbol.iterator]() {
return {
// Arrow function so that we can use the outer `this`
next: () => {
if (this.#index >= this.#values.length) {
return {done: true};
}
const value = this.#values[this.#index];
this.#index++;
return {done: false, value};
},
};
}
}
// legacyIterable is an iterable
const legacyIterable = new ValueIterable('a', 'b', 'c');
assert.deepEqual(
Array.from(new ValueIterable('a', 'b', 'c')),
['a', 'b', 'c']
);
// But its iterators are not instances of Iterator
const legacyIterator = legacyIterable[Symbol.iterator]();
assert.equal(
legacyIterator instanceof Iterator, false
);
If we want ValueIterable
to support the Iterator
API, we have to make its iterators instances of Iterator
:
class ValueIterable {
// ···
[Symbol.iterator]() {
return {
__proto__: Iterator.prototype,
next: () => {
// ···
},
};
}
}
This is another option (albeit a less efficient one):
class ValueIterable {
// ···
[Symbol.iterator]() {
return Iterator.from({
next: () => {
// ···
},
});
}
}
We can also create a class for iterators:
class ValueIterable {
#values;
#index = 0;
constructor(...values) {
this.#values = values;
}
[Symbol.iterator]() {
const outerThis = this;
// Because ValueIterator is nested, it can access the private fields of
// ValueIterable
class ValueIterator extends Iterator {
next() {
if (outerThis.#index >= outerThis.#values.length) {
return {done: true};
}
const value = outerThis.#values[outerThis.#index];
outerThis.#index++;
return {done: false, value};
}
}
return new ValueIterator();
}
}
Map.groupBy()
groups the items of an iterable into Map entries whose keys are provided by a callback:
assert.deepEqual(
Map.groupBy([0, -5, 3, -4, 8, 9], x => Math.sign(x)),
new Map().set(0, [0]).set(-1, [-5,-4]).set(1, [3,8,9])
);
The items to be grouped can come from any iterable:
function* generateNumbers() {
yield 2;
yield -7;
yield 4;
}
assert.deepEqual(
Map.groupBy(generateNumbers(), x => Math.sign(x)),
new Map().set(1, [2,4]).set(-1, [-7])
);
There is also Object.groupBy()
which produces an object instead of a Map:
assert.deepEqual(
Object.groupBy([0, -5, 3, -4, 8, 9], x => Math.sign(x)),
{'0': [0], '-1': [-5,-4], '1': [3,8,9], __proto__: null}
);
Map.groupBy()
and Object.groupBy()
.groupBy()
(see example later in this section)?
The Promise combinator Promise.allSettled()
returns Arrays such as the following one:
const settled = [
{ status: 'rejected', reason: 'Jhon' },
{ status: 'fulfilled', value: 'Jane' },
{ status: 'fulfilled', value: 'John' },
{ status: 'rejected', reason: 'Jaen' },
{ status: 'rejected', reason: 'Jnoh' },
];
We can group the Array elements as follows:
const {fulfilled, rejected} = Object.groupBy(settled, x => x.status); // (A)
// Handle fulfilled results
assert.deepEqual(
fulfilled,
[
{ status: 'fulfilled', value: 'Jane' },
{ status: 'fulfilled', value: 'John' },
]
);
// Handle rejected results
assert.deepEqual(
rejected,
[
{ status: 'rejected', reason: 'Jhon' },
{ status: 'rejected', reason: 'Jaen' },
{ status: 'rejected', reason: 'Jnoh' },
]
);
For this use case, Object.groupBy()
works better because we can use destructuring (line A).
In the next example, we’d like to group persons by country:
const persons = [
{ name: 'Louise', country: 'France' },
{ name: 'Felix', country: 'Germany' },
{ name: 'Ava', country: 'USA' },
{ name: 'Léo', country: 'France' },
{ name: 'Oliver', country: 'USA' },
{ name: 'Leni', country: 'Germany' },
];
assert.deepEqual(
Map.groupBy(persons, (person) => person.country),
new Map([
[
'France',
[
{ name: 'Louise', country: 'France' },
{ name: 'Léo', country: 'France' },
]
],
[
'Germany',
[
{ name: 'Felix', country: 'Germany' },
{ name: 'Leni', country: 'Germany' },
]
],
[
'USA',
[
{ name: 'Ava', country: 'USA' },
{ name: 'Oliver', country: 'USA' },
]
],
])
);
For this use case, Map.groupBy()
is a better choice because we can use arbitrary keys in Maps whereas in objects, keys are limited to strings and symbols.
Exercise: Using
Map.groupBy()
for an Array of objects
exercises/sync-iteration/count-cities_test.mjs
These data structures are iterable:
The following data structures have the methods .keys()
, .values()
, and .entries()
that return iterables that are not Arrays:
As an aside – the following static methods list property keys, values and entries (they are not normal methods because those can be accidentally overridden). They return Arrays.
Object.keys(obj)
Object.values(obj)
Object.entries(obj)
Synchronous generator functions and methods expose their yielded values via iterable objects that they return:
/** Synchronous generator function */
function* createSyncIterable() {
yield 'a';
yield 'b';
yield 'c';
}
assert.deepEqual(
Array.from(createSyncIterable()),
['a', 'b', 'c']
);
This section lists constructs that consume data via synchronous iteration.
The for-of
loop:
for (const x of iterable) { /*···*/ }
Spreading (via ...
) into Array literals and function calls:
const arr = ['a', ...iterable, 'z'];
func('a', ...iterable, 'z');
Destructuring via an Array pattern:
const [x, y] = iterable;
yield*
:
function* generatorFunction() {
yield* iterable;
}
const obj = Object.fromEntries(iterableOverKeyValuePairs);
const arr = Array.from(iterable);
Alternative – spreading:
const arr = [...iterable];
new Map()
and new WeakMap()
:
const m = new Map(iterableOverKeyValuePairs);
const wm = new WeakMap(iterableOverKeyValuePairs);
new Set()
and new WeakSet()
:
const s = new Set(iterableOverElements);
const ws = new WeakSet(iterableOverElements);
Promise combinator functions: Promise.all()
etc.
const promise1 = Promise.all(iterableOverPromises);
const promise2 = Promise.race(iterableOverPromises);
const promise3 = Promise.any(iterableOverPromises);
const promise4 = Promise.allSettled(iterableOverPromises);
Iterator
ES2025The methods of class Iterator
let us process data incrementally. Let’s explore where we can use them.
Iterator.from(iterable)
always returns instances of Iterator
(converting non-instances to instances as needed).
iterable[Symbol.iterator]()
returns an iterator:
Iterator
.
Iterator
.
Arrays, Typed Arrays, Sets and Maps have additional methods that return iterators:
Array.prototype.keys()
returns an iterator over numbers.
Array.prototype.values()
returns an iterator.
Array.prototype.entries()
returns an iterator over key-value pairs. The keys are numbers.
Set.prototype.values()
returns an iterator.
Set.prototype.keys()
returns an iterator. Equivalent to .values()
.
Set.prototype.entries()
returns an iterator over value-value pairs (i.e., both components of the pair are the same value).
Map.prototype.keys()
returns an iterator.
Map.prototype.values()
returns an iterator.
Map.prototype.entries()
returns an iterator over key-value pairs.
The following methods return iterators:
String.prototype.matchAll()
returns an iterator over match objects.
Generators also return iterators:
function* gen() {}
assert.equal(
gen() instanceof Iterator,
true
);
Iterator.*
Iterator.from(iterableOrIterator)
returns an iterator that is guaranteed to be an instance of Iterator
. If the parameter is a legacy iterable or a legacy iterator, it wraps the result so that it becomes an instance of Iterator
.
Iterator.prototype.*
: methods that pass indices to callbacksSome of the iterator methods keep a counter for the iterated values and pass it on to their callbacks:
.every()
.filter()
.find()
.flatMap()
.forEach()
.map()
.reduce()
.some()
Iterator.prototype.*
: methods that return iteratorsIterator.prototype.drop(limit)
ES2025
Iterator<T>.prototype.drop(limit: number): Iterator<T>
This method returns an iterator that with all values of iterator
, except for the first limit
ones. That is, iteration starts when the iteration counter is limit
.
assert.deepEqual(
Iterator.from(['a', 'b', 'c', 'd']).drop(1).toArray(),
['b', 'c', 'd']
);
Iterator.prototype.filter(filterFn)
ES2025 | Callback gets counter
Iterator<T>.prototype.filter(
filterFn: (value: T, counter: number) => boolean
): Iterator<T>
This method returns an iterator whose values are the values of iterator
for which filterFn
returns true
.
assert.deepEqual(
Iterator.from(['a', 'b', 'c', 'd']).filter(x => x <= 'b').toArray(),
['a', 'b']
);
Iterator.prototype.flatMap(mapFn)
ES2025 | Callback gets counter
Iterator<T>.prototype.flatMap<U>(
mapFn: (value: T, counter: number) => Iterable<U> | Iterator<U>
): Iterator<U>
This method returns an iterator whose values are the values of the iterables or iterators that are the results of applying mapFn
to the values of iterator
.
assert.deepEqual(
Iterator.from(['a', 'b', 'c', 'd'])
.flatMap((value, counter) => new Array(counter).fill(value))
.toArray(),
['b', 'c', 'c', 'd', 'd', 'd']
);
For more information see the section on the Array method with the same name: “.flatMap()
: Each input element produces zero or more output elements ES2019” (§34.14.3).
Iterator.prototype.map(mapFn)
ES2025 | Callback gets counter
Iterator<T>.prototype.map<U>(
mapFn: (value: T, counter: number) => U
): Iterator<U>
This method returns an iterator whose values are the result of applying mapFn
to the values of iterator
.
assert.deepEqual(
Iterator.from(['a', 'b', 'c', 'd']).map(x => x + x).toArray(),
['aa', 'bb', 'cc', 'dd']
);
Iterator.prototype.take(limit)
ES2025
Iterator<T>.prototype.take(limit: number): Iterator<T>
This method returns an iterator with the first limit
values of iterator
.
assert.deepEqual(
Iterator.from(['a', 'b', 'c', 'd']).take(1).toArray(),
['a']
);
Iterator.prototype.*
: methods that return booleansIterator.prototype.every(fn)
ES2025 | Callback gets counter
Iterator<T>.prototype.every(
fn: (value: T, counter: number) => boolean
): boolean
This method returns true
if fn
returns true
for every value of iterator
. Otherwise, it returns false
.
assert.equal(
Iterator.from(['a', 'b', 'c', 'd']).every(x => x === 'c'),
false
);
Iterator.prototype.some(fn)
ES2025 | Callback gets counter
Iterator<T>.prototype.some(
fn: (value: T, counter: number) => boolean
): boolean
This method returns true
if fn
returns true
for at least one value of iterator
. Otherwise, it returns false
.
assert.equal(
Iterator.from(['a', 'b', 'c', 'd']).some(x => x === 'c'),
true
);
Iterator.prototype.*
: methods that return other kinds of valuesIterator.prototype.find(fn)
ES2025 | Callback gets counter
Iterator<T>.prototype.find(
fn: (value: T, counter: number) => boolean
): T
This method returns the first value of iterator
for which fn
returns true
. If there is no such value, it returns undefined
.
assert.equal(
Iterator.from(['a', 'b', 'c', 'd']).find((_, counter) => counter === 1),
'b'
);
Iterator.prototype.reduce(reducer, initialValue?)
ES2025 | Callback gets counter
Iterator<T>.prototype.reduce<U>(
reducer: (accumulator: U, value: T, counter: number) => U,
initialValue?: U
): U
This method uses the function reducer
to combine the values of iterator
into a single value.
Example – concatenating the strings of an iterator:
assert.deepEqual(
Iterator.from(['a', 'b', 'c', 'd']).reduce((acc, v) => acc + v),
'abcd'
);
Example – computing the minimum of a Set of numbers:
const set = new Set([3, -2, -5, 4]);
assert.equal(
set.values().reduce((min, cur) => cur < min ? cur : min, Infinity),
-5
);
For more information see the section on the Array method with the same name: “.reduce()
: computing a summary for an Array” (§34.15).
Iterator.prototype.toArray()
ES2025
Iterator<T>.prototype.toArray(): Array<T>
This method returns the values of iterator
in an Array.
assert.deepEqual(
Iterator.from(['a', 'b', 'c', 'd']).toArray(),
['a', 'b', 'c', 'd']
);
Iterator.prototype.*
: other methodsIterator.prototype.forEach(fn)
ES2025 | Callback gets counter
Iterator<T>.prototype.forEach(
fn: (value: T, counter: number) => void
): void
This method applies fn
to each value in iterator
.
const result = [];
Iterator.from(['a', 'b', 'c', 'd']).forEach(x => result.unshift(x))
assert.deepEqual(
result,
['d', 'c', 'b', 'a']
);