An extension of JavaScript's built-in Iterator class, with several utility functions for transforming and aggregating sequences of values.
Contributions are welcome. 🤗
- 30+ utility methods
- Lazy operations
- Zero dependencies 🎉
- Fully typed with TypeScript
- 100% test coverage
npm install extra-iteratorThis package extends the built-in Iterator class and uses the Iterator Helpers API.
Use it in a runtime that already supports iterator helpers, or load an appropriate polyfill.
import { ExtraIterator } from 'extra-iterator';
const range = ExtraIterator.range(1, 6)
.map(n => n * 2)
.toArray();
console.log(range); // [1, 2, 3, 4, 5]import { ExtraIterator } from 'extra-iterator';
const result = ExtraIterator.concat([1, 2, 3], [4, 5, 6])
.dropWhile(n => n < 3)
.take(3)
.loop(4)
.toArray();
console.log(result); // [3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5]import { ExtraIterator } from 'extra-iterator';
const example = {
first: 1,
second: 2,
third: 3,
};
const result = ExtraIterator.from(Object.entries(example))
.append(['fourth', 4])
.interposeWith(([leftKey, leftValue], [rightKey, rightValue]) =>
[`${leftKey}_${rightKey}`, (leftValue + rightValue) / 2]
)
.collect(Object.fromEntries);
console.log(result);
// {
// first: 1,
// first_second: 1.5,
// second: 2,
// second_third: 2.5,
// third: 3,
// third_fourth: 3.5,
// fourth: 4
// }import { ExtraIterator } from 'extra-iterator';
const fromIterable = ExtraIterator.from(new Set([1, 2, 3]));
const fromGenerator = ExtraIterator.from(function*() {
yield 'a';
yield 'b';
}());
const fromArrayLike = ExtraIterator.from({ 0: 'x', 1: 'y', length: 2 });
console.log(fromGenerator.toArray()); // ['a', 'b']
console.log(fromArrayLike.toArray()); // ['x', 'y']Transformation methods are lazy. Nothing happens until you consume the iterator.
const iter = ExtraIterator.from([1, 2, 3])
.map(value => value * 2)
.withEach(value => console.log('seen:', value));
// No output yet.
console.log(iter.take(2).toArray());
// seen: 2
// seen: 4
// [2, 4]Like native iterators, ExtraIterator instances are generally one-shot. Once consumed, the values are gone unless you
create a new iterator.
import { ExtraIterator } from 'extra-iterator';
const evens = ExtraIterator.count({ start: 0, increment: 2 })
.take(5)
.toArray();
console.log(evens); // [0, 2, 4, 6, 8]import { ExtraIterator } from 'extra-iterator';
const values = ExtraIterator.from([0, [1, [2, [3, 4]]], [5, [6]], 7])
.flat()
.toArray();
console.log(values); // [0, 1, 2, 3, 4, 5, 6, 7]import { ExtraIterator } from 'extra-iterator';
const chunks = ExtraIterator.from([1, 1, 2, 3, 3, 3, 2, 2])
.chunkWith((left, right) => left === right)
.toArray();
console.log(chunks); // [[1, 1], [2], [3, 3, 3], [2, 2]]import { ExtraIterator } from 'extra-iterator';
const commonAncestor = ExtraIterator.from(document.querySelectorAll('p'))
.map(element => ExtraIterator.from(function*() {
for (let node = element.parentNode; node; node = node.parentNode) {
yield node;
}
}()))
.map(ancestorList => ancestorList.toArray().reverse())
.zip()
.takeWhile(nodes => new Set(nodes).size === 1)
.last()
.first();See full API documentation at: leonardoraele.github.io/extra-iterator
ExtraIterator.from(source)- Iterates over any iterable object or array-like object.ExtraIterator.empty()- Creates an empty iterator.ExtraIterator.single(value)- Creates an iterator with a single value.ExtraIterator.count()- An infinite incremental number iterator.ExtraIterator.range(start, end)- Iterates over a specific range.ExtraIterator.repeat(value)- An iterator that repeats a single value infinitely.ExtraIterator.random()- An iterator that yields infinite random numbers.ExtraIterator.randomBytes()- An iterator that yields infinite chunks of random bytes.ExtraIterator.zip(...sources)- Iterates over multiple sequences simultaneously.
These native iterator-helper methods work just like the built-in methods, but return chainable ExtraIterator instances instead of built-in Iterator objects.
filter()flatMap()map()take()drop()
take() and drop() also support negative counts:
take(n)keeps the firstnelements of the iterator (or the last ones ifnis negative), and discard the rest.drop(n)discards the firstnelements (or the last ones ifnis negative), and keep the rest.
flat()- Similar toArray.prototype.flat(), but recursive.unique()- Filters repeated elements out of the iterator.compact()- Filtersnullandundefinedvalues out of the iterator.append(value)- Appends one value to the end of the iterator.prepend(value)- Prepends one value to the beginning of the iterator.concat(iterable)- Concatenates multiple values to the end of the iterator.prependAll(iterable)- Prepends multiple values to the beginning of the iterator.takeWhile(predicate)- Similar toIterator.prototype.take(), but with a condition instead of a fixed count.dropWhile(predicate)- Similar toIterator.prototype.drop(), but with a condition instead of a fixed count.chunk(size)- Groups the elements into chunks of fixed size.chunkWith(predicate)- Groups adjacent elements based on a predicate function.zip(others)- Creates a new iterator that yields the values of this and the other iterators in tuples.interpose(separator)- Adds a separator value between each pair of adjacent elements.interposeWith(separatorProvider)- Adds a separate value between each pair of elements based on a function.interleave(other)- Interleave the values of this iterator with those of another iterator.splice(startIndex, deleteCount, ...newItems)- Similar toArray.prototype.splice().defaultIfEmpty(provider)- If the iterator is empty, adds a default value to it.loop(n = Infinity)- Expands the iterator by repeating its valuesntimes.withEach(callback)- Similar toIterator.prototype.forEach(), but returnsthisiterator.
These methods consume the iterator and return a final value instead of another iterator:
first()- Returns the first value orundefinedif empty.last()- Returns the last value orundefinedif empty.at(index)- Returns the value at the index, orundefined.groupBy(callback)- Similar toObject.groupBy().toMap(callback)- Similar toMap.groupBy().toSet()- Creates aSetobject with the elements of this iterator.toChainOfResponsibilityFunction(invokeHandler)collect(callback)- Reduce the iterator to a single value by calling a callback function.sum()- Sum all numbers in the iterator.count()- Returns the number of elements in the iterator.testUnique()- Checks whether the iterator has duplicated elements.
You also still have the native consuming helpers provided by iterator helpers, such as reduce(), some(), every(),
find(), forEach(), and toArray().
An optional, chainable helper method that transforms any iterable object into an ExtraIterator.
import { toExtra } from 'extra-iterator/to-extra';
const result = [1, 2, 3]
[toExtra]() // The `[toExtra]()` method transforms the built-in array into an `ExtraIterator` object...
.prepend(0) // Allowing you to chain into the new helper methods:
.splice(2, 0, 0.5)
.drop(-2)
.loop(3)
.toArray();
console.log(result);
// [0, 0.5, 1, 0, 0.5, 1, 0, 0.5, 1]By importing the optional extra-iterator/to-extra submodule, you opt-in to expanding the global Object prototype
with the [toExtra]() method as a side-effect.
This side-effect is optional. If you don't want to "polute" the global prototypes, you can still use this package as
normal. Simply don't import extra-iterator/to-extra and the global prototypes will remain unchanged.
This project is licensed under the MIT License. See the LICENSE.txt file for the full license text.