Package detail

iter-tools-es

iter-tools119.4kMIT7.5.4

The iterable toolbox

iterator, iterable, generator, Map

readme

iter-tools

build is passing most days coverage is nearly 100% semver compliant come say hi on discord!

iter-tools provides a comprehensive suite of utility methods for working with javascript iterables and async iterables. Iterables offer an abstraction for describing sequences of values. If you're not sure if iter-tools is the right library for you, check out our features.

Usage

import { filter } from 'iter-tools-es';
const { filter } = require('iter-tools-es');

This package uses es2018 syntax. If your environment does not support es2018, use the iter-tools package.

API

Please read our API docs!

Historical docs are markdown files on github. For 6.x docs look at tags on the history of README.md. For 7.x versions look at tags on the history of API.md.

The high level

If you're not already familiar with what iterator tools can offer you and why you may want to use them, read on.

While I try to include the most important concepts here, additional content is present also in the github wiki, including a cookbook providing author and user-submitted examples of ways to use the library to accomplish common tasks.

Why use iterables?

Iterables will never replace arrays, but they have several advantages which make them the future for high-level work in Javascript.

  • Defining an API in terms of iterables frees you from needing to worry about how data is stored. If you only expect iterables your code will not break when the data you expect changes from being an Array to being a Set or a computed representation of values stored elsewhere (or not stored at all). When only the iterable contract is used you will also be freed from other worries like a callee unexpectedly mutating a caller's data.

  • Iterables are lazy, which means they do work only when the results of that work are needed. Sometimes this can save you having to do any work! This also allows iterables to definite infinite sequences which are not possible to store in an array. range() for example returns the sequence of all positive integers.

  • The use of small, short-lived objects with predictable shapes (like the { value, done } object returned by iterator.next()) is preferable in situations where responsiveness is a bigger concern than raw speed, most notably web applications (and especially inside of tight loops such as mouse event handlers). This is because generational garbage collectors are optimized for such objects, and can free their memory in imperceptably-fast minor GCs.

What is iterable?

All javascript data types designed for the storage of data (Array, Map, and Set) are iterable, and any object or class can be made iterable by implementing a Symbol.iterator method which returns an iterator. For more reading on what is iterable and how to define iterables, see the docs for isIterable.

Using iterables

Iterables are usually consumed with the for..of loop syntax or the destructuring assignment syntax (e.g. const [a, b] = iterable). They can also be consumed by iter-tools methods.

Some iterables are also iterators, earning them the name IterableIterator. Any iterables returned by iter-tools methods (e.g. filter) are iterable iterators because they are implemented using generator functions. This means they can be used in one of two ways:

import { filter, notUndefined } from 'iter-tools-es';
// as an iterable:
for (const value of filter(notUndefined, iterable)) {
  /* ... */
}
// or as an iterator:
class SparseArray extends Array {
  [Symbol.iterator]() {
    return filter(notUndefined, this);
  }
}

Features

iter-tools is one of a handful of libraries that offer some common functionality like map and filter methods. It offers these features in addition to the common functionality:

  • It can eliminate a whole class of null pointer errors by treating null and undefined as empty iterables.
  • Any Iterable returned is an IterableIterator.
  • Methods support currying, making them ideal for usage with provided methods like pipe or compose, or in a variety of other situations.
import { filter } from 'iter-tools-es';
const { filter } = require('iter-tools-es');

This package uses es2018 syntax. If your environment does not support es2018, use the iter-tools package.

Individual Methods

It is possible to import individual methods from the library. This will make the resulting program or application faster to initialize as it avoids the need to parse the whole library (including parts of it which may not be used). Note that this will not be nececssary for code which will be bundled prior to shipping, as modern versions of rollup and webpack will remove the unused code. Importing a single method looks like this:

const notUndefined = require('iter-tools-es/methods/not-undefined');

explode.macro

If you happen to be transpiling the code and have use of the fantastic babel-plugin-macros, you can generate single-method imports effortlessly like so:

import { filter, map } from 'iter-tools-es/explode.macro';

// which transpiles to:
import filter from 'iter-tools-es/methods/filter';
import map from 'iter-tools-es/methods/map';

__methods

Methods whose names begin with __ (double underscore) are safe to use – they are part of the library's public API. They are intended for use by extenders of the library and in tight loops where performance is critical.

There are some differences in the order in which arguments are passed, but these are documented along with the method's other overloads. Other less visible differences are:

__ methods are not curried. You must pass all their arguments in a single call
__ methods (aside from __wrap) do not treat null and undefined as iterables.
__ methods do not permit sync iterables to be used in place of async iterables (this could change in the future).
__ methods return singleton iterable iterators. You can only loop over the results once.
__ methods may expose implementation internals which are not part of the documented public API. Code using undocumented APIs is subject to breakage in any release, in accordance with the semver specification.
__ methods do not have type definitions.

Roadmap

Some major improvements are still to come. They are:

  • UMD support
  • @iter-tools/regex for evaluating regular expressions against iterables
  • @iter-tools/unicode for turning strings into iterables of graphemes or extended grapheme clusters
  • Flow types

Issues and limitations

Some methods in iter-tools consume an entire iterable, such as arrayFrom, last, or cycle. These methods will not terminate if you pass them an infinite iterable such as range(). Eventually we may have a better system for warning you in the circumstances when we know you've done something obviously wrong like cycle(range()), but this does not exist yet.

iter-tools does not aim to provide all possible operations. If a method only makes sense when its input or output is an array, iter-tools probably does not have an implementation. groupBy is probably the most common example of this. I may in the future create an @iter-tools/array package for such methods if there is sufficient demand.

Acknowledgements

I give a lot of credit to the great itertools Python library. This package doesn't want to be a mere port, but rather aims to solve the same problems as Python's library.

Many thanks to Maurizio Lupo (sithmel) for doing the initial porting and development from Python's itertools, for being accepting of new contributors (and patient with us), and for eventually passing maintainership along.

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

[UNRELEASED]

[7.5.0] - 2022-7-23

Added

Methods

  • findBest, asyncFindBest
  • findBestOr, asyncFindBestOr
  • firstHighest
  • lastHighest
  • firstLowest
  • lastLowest

[7.4.0] - 2022-6-20

Added

Methods

  • distinct

[7.3.0] - 2022-3-23

Changed

  • A Forkerator can now be used as an iterable.

[7.2.0] - 2021-12-22

Added

Methods

  • isString, notString
  • isArray, notArray

[7.1.0] - 2021-03-20

Added

Methods

  • forkerate, asyncForkerate

[7.0.1] - 2020-12-12

Fixed

require('iter-tools') in node 12+ (ERR_REQUIRE_ESM)

[7.0.0] - 2020-12-12

Removed

Folders

  • es2015: It is expected that most users will switch to import ... from 'iter-tools-es'.

Methods

  • joinAsString, asyncJoinAsString (Instead use str(join(...)))
  • joinAsStringWith, asyncJoinAsStringWith (Instead use str(joinWith(sep, ...)))
  • regexpExec
  • nullOr, nullOrAsync (Instead use peekerate)
  • asyncFlatMapParallel
  • asyncMapParallel (Instead use asyncBuffer(n, asyncMap(fn, iterable)))
  • asyncFilterParallel (Instead use asyncBuffer(n, asyncFilter(fn, iterable)))
  • group, asyncGroup (splitGroups is a drop-in replacement)
  • combinations, combinationsWithReplacement, permutations, product (use @iter-tools/combinatorics)
  • leadingWindow, asyncLeadingWindow (Instead use windowAhead. Only arg order changed)
  • trailingWindow, asyncTrailingWindow (Instead use windowBehind. Only arg order changed)

Arguments

  • n from fork and asyncFork. Use destructuring or call return() on the forks iterable.

Exports

  • InterleaveBuffer, AsyncInterleaveBuffer

Overloads

  • { size, filler } from leadingWindow, asyncLeadingWindow, trailingWindow, asyncTrailingWindow. Instead use e.g. leadingWindow(size, { filler }).
  • splitGroups(null), asyncSplitGroups(null). (Use splitGroups(iterable))

Renamed

Folders

  • es2018 moved to separate package: iter-tools-es.
    • e.g. import { map } from 'iter-tools/es2018 is now import { map } from 'iter-tools-es

Methods

  • last, asyncLast to takeLast, asyncTakeLast
  • lastOr, asyncLastOr to takeLastOr, asyncTakeLastOr
  • groupBy, asyncGroupBy to splitGroups, asyncSplitGroups
  • splitAt, asyncSplitAt to bisect, asyncBisect
  • splitWith, asyncSplitWith to splitWhen, asyncSplitWhen
  • *subseq to *seq
    • startsWithSubseq, asyncStartsWithSubseq to startsWithSeq, asyncStartsWithSeq
    • startsWithAnySubseq, asyncStartsWithAnySubseq to startsWithAnySeq, asyncStartsWithAnySeq
    • includesSubseq, asyncIncludesSubseq to includesSeq, asyncIncludesSeq
    • includesAnySubseq, asyncIncludesAnySubseq to includesAnySeq, asyncIncludesAnySeq
    • splitOnSubseq, asyncSplitOnSubseq to splitOnSeq, asyncSplitOnSeq
    • splitOnAnySubseq, asyncSplitOnAnySubseq to splitOnAnySeq, asyncSplitOnAnySeq
    • joinWithSubseq, asyncJoinWithSubseq to joinWithSeq, asyncJoinWithSeq

Changed

  • interleave and asyncInterleave now wrap sources with Peekerator instead of InterleaveBuffer. Change function* (canTakeAny, ...buffers) { while(canTakeAny()) { ... } } to function* (all, ...peekrs) { while(!all.done) { ... } }.
  • last and lastOr no longer have O(1) optimizations for array inputs. Instead use arrayLast or arrayLastOr.
  • batch, asyncBatch now yield batches which are iterables but not arrays. The batches must be consumed in order. For the old behavior use map(toArray, batch(...)).
  • *any and *anySubseq methods now require the possible values or subseqs to be passed as a non-nullable array. To replicate the old behavior change startsWithAny(valuesIterable) to startsWithAny(wrap([...valuesIterable]).
  • includesAny, startsWithAny (and async and seq variants) now return false when nothing is being searched for, e.g. includesAny([], iterable) === false.
  • windowAhead

Added

Methods

  • arrayFirst, arrayFirstOr
  • arrayLast, arrayLastOr
  • arrayReverse
  • interposeSeq, asyncInterposeSeq
  • peekerate, asyncPeekerate
  • spliterate, asyncSpliterate
  • spliterateGrouped, asyncSpliterateGrouped
  • window, asyncWindow
  • stringFrom, stringFromAsync
  • isNull, notNull
  • isUndefined, notUndefined
  • isNil, notNil
  • isObject, notObject
  • isIterable, notIterable, isAsyncIterable, notAsyncIterable
  • isWrappable, notWrappable, isAsyncWrappable, notAsyncWrappable
  • isLoopable, notLoopable, isAsyncLoopable, notAsyncLoopable
  • deepEqual, asyncDeepEqual
  • windowAhead, windowBehind ,asyncWindowAhead, asyncWindowBehind

Overloads

  • bisect(predicate), asyncBisect(predicate)

Aliases

  • str for stringFrom, asyncStr for stringFromAsync

Arguments

  • useFiller option for leadingWindow and asyncLeadingWindow.
  • proto passed to Object.create for objectFrom and objectFromAsync (also toObject and asyncToObject).
  • same to equal, includes, includesAny, includesSeq, includesAnySeq, startsWith, startsWithAny, startsWithSeq, startsWithAnySeq, splitOn, splitOnAny, splitOnSeq, splitOnAnySeq (and async variants: asyncEqual, asyncIncludes, asyncIncludesAny, asyncIncludesSeq, asyncIncludesAnySeq, asyncStartsWith, asyncStartsWithAny, asyncStartsWithSeq, asyncStartsWithAnySeq, asyncSplitOn, asyncSplitOnAny, asyncSplitOnSeq, asyncSplitOnAnySeq)

Instance Methods

  • Peekerator: asIterator method

Changed

  • Removed O(1) array optimizations from last and lastOr
  • Factorial no attempts to use BigNum internally. Some inputs may now cause combinations().size or combinationsWithReplacement().size to overflow during computation.

Instance Methods

  • Peekerator: advance and return methods now return this (was undefined).

[7.0.0-rc.0] - 2019-12-13

Removed

Methods

  • iter, asyncIter
  • iterable, asyncIterable
  • tee, asyncTee
  • execute, asyncExecute (Instead use map(_ => callback(), range()) (or asyncMap(...)))
  • merge, asyncMerge (Instead use collate, roundRobin, interleave (or async equivalents))
  • splitLines, asyncSplitLines (Instead use asyncMap(asyncJoinAsString, asyncSplitOnAnySubseq(['\r\n', '\n'], asyncFlat(fileChunks))) or splitOnAnySubseq(['\r\n', 'n'], fileString). Sorry, a better way is coming soon!)
  • regexpSplit (Instead use splitWith(regexp, str))
  • cursor, asyncCursor (Instead use leadingWindow or trailingWindow (or async equivalents))
  • keys (Instead use objectKeys(obj) or wrapKeys({ keys(); })))
  • values (Instead use objectValues(obj) or wrapValues({ values(); })))
  • entries (Instead use objectEntries(obj) or wrapEntries({ entries(); })))

Overloads

  • size({ size }) and size(array) (Instead use getSize)
  • cycle(times, iterable), asyncCycle(times, iterable) and repeat(times, value) (Instead use cycleTimes(n, iterable) (or asyncCycleTimes), and repeatTimes(n, iterable))
  • consume(callback, iterable), asyncConsume(callback, iterable) (Instead use forEach)

Aliases

  • count was an alias for range
  • chain, asyncChain were aliases for concat, asyncConcat
  • zipLongest, asyncZipLongest were aliases for zipAll, asyncZipAll

Arguments

  • concurrency from asyncMap, asyncFilter.
  • Extra arguments from compose, pipe, pipeExec. Previously all initial arguments were given to the outermost composed function.

Helper methods

  • mergeByComparison, mergeByChance, mergeByPosition, asyncMergeByComparison, asyncMergeByChance, asyncMergeByPosition

Renamed

Methods

  • pipe to execPipe

Changed

  • import 'iter-tools/es5/method' should now be import 'iter-tools/method'.
  • IMPORTANT; BREAKING: slice(n) is now equivalent to [].slice(n), not [].slice(0, n). You should now write slice(0, n, iterable).
  • IMPORTANT; BREAKING: repeat order of arguments changed. You must now write repeat(3, 'x') instead of repeat(x, 3).
  • All functions return iterables that can consumed multiple times.
  • It is now an error to make an empty partial application, e.g. map().
  • size(iterable) now always consumes iterable. Use getSize if you know this is unnecessary.
  • takeSorted and asyncTakeSorted: Both n and comparator arguments are now optional.
  • enumerate and asyncEnumerate: optional starting idx is now specified before iterable.
  • Optional configuration arguments can no longer be undefined. This was at odds with considering undefined as a valid iterable.
  • range can now called as either range(end) or range(start, end, step). This matches Python.
  • zipAll now takes optional filler argument to use in place of values from exhausted iterables.
  • fork and asyncFork now take an extra optional argument: the number of forks.
  • permutations, combinations, combinationsWithReplacement: order of arguments is changed. Can now be curried.
  • permutations, combinations, combinationsWithReplacement, and product: getSize() is now just size.
  • groupBy() and asyncGroupBy(null) now return a partial application instead of an iterable.
  • groupBy now throws an error if the groups are accessed out of order.
  • asyncBuffer now starts buffering immediately instead of when the first item is taken.
  • For most Typescript generic method types, the order of the generics has changed. (Note: this only matters if you are explicitly providing values for the generics.)

Added

Methods

  • pipe
  • collate, asyncCollate
  • roundRobin, asyncRoundRobin
  • call, apply
  • wrap, asyncWrap
  • wrapKeys
  • wrapValues
  • wrapEntries
  • objectKeys
  • objectValues
  • objectEntries
  • toObject, asyncToObject
  • findOr, asyncFindOr
  • firstOr, asyncFirstOr
  • last, asyncLast
  • lastOr, asyncLastOr
  • take, asyncTake
  • drop, asyncDrop
  • when
  • isEmpty, asyncIsEmpty
  • asyncMapParallel, asyncFilterParallel, asyncFlatMapParallel
  • interleave, asyncInterleave, asyncInterleaveReady
  • equal, asyncEqual
  • includes, includesAny, includesSubseq, includesAnySubseq, asyncIncludes, asyncIncludesAny, asyncIncludesSubseq, asyncIncludesAnySubseq
  • startsWith, startsWithAny, startsWithSubseq, startsWithAnySubseq, asyncStartsWith, asyncStartsWithAny, asyncStartsWithSubseq, asyncStartsWithAnySubseq
  • splitOn, splitOnAny, splitOnSubseq, splitOnAnySubseq, asyncSplitOn, asyncSplitOnAny, asyncSplitOnSubseq, asyncSplitOnAnySubseq
  • split, asyncSplit
  • splitWith, asyncSplitWith
  • splitAt, asyncSplitAt
  • join, asyncJoin
  • joinWith, joinWithSubseq, asyncJoinWith, asyncJoinWithSubseq
  • joinAsString, joinAsStringWith, asyncJoinAsString, asyncJoinAsStringWith
  • group, asyncGroup
  • explode, asyncExplode
  • reverse, asyncReverse
  • leadingWindow, asyncLeadingWindow
  • trailingWindow, asyncTrailingWindow
  • cycleTimes, asyncCycleTimes
  • repeatTimes
  • getSize

Aliases

  • arrayFrom, arrayFromAsync as aliases for toArray, asyncToArray
  • objectFrom, objectFromAsync as aliases for toObject, asyncToObject

Arguments

  • [optional] n (count) for fork and asyncFork
  • [optional] n (count) for cycle and asyncCycle
  • [optional] filler (specified as { filler }) for zip and zipAll
  • [optional] notFoundValue for find and asyncFind

Overloads

  • range(start, end, [step]) (Matches Python.)

Deprecated

Overloads

  • groupBy(null, source) and asyncGroupBy(null, source). Instead use group(source) and asyncGroup(source).
  • consume(callback, source) and asyncConsume(callback, source). Instead use forEach(callback, source) and asyncForEach(callback, source).

    Fixed

  • A wide variety of Typescript type bugs were identified and squashed. You can see the full list on Github

[6.2.3] - 2019-01-23

Added

Methods

  • flat, asyncFlat
  • cursor, asyncCursor
  • merge, asyncMerge
  • pipe
  • partition, asyncPartition

Helper methods

  • mergeByComparison, mergeByChance, mergeByPosition, asyncMergeByComparison, asyncMergeByChance, asyncMergeByPosition

Arguments

  • [optional] concurrency for asyncMap, asyncFilter

Fixed

  • async function can take an async callback
  • find and asyncFind: now return undefined when an item has not been found

Changed

  • combinatory function generators return an iterable with an extra method to calculate the size

[6.1.7] - 2018-11-06

Fixed

  • Added polyfills to transpiled versions

[6.1.6] - 2018-11-06

Fixed

  • Bump all deps (code vulnerability)

[6.1.5] - 2018-10-23

Fixed

  • Removed clone-regexp dependency

[6.1.4] - 2018-09-22

Fixed

  • Typescript definitions

[6.1.3] - 2018-09-12

Fixed

  • Typescript definitions

[6.1.0] - 2018-09-09

Removed

Methods

  • asyncMapBatch

Arguments

  • filler argument from zipLongest, asyncZipLongest

Changed

  • All methods: Object parameters are no longer implicitly treated as iterables, and will throw errors.
  • All curried methods: passing null and undefined as the iterable will always result in those values being coerced to iterables. Currying happens based on arguments.length.
  • compose([...fns]) => compose(...fns)
  • asyncRegexpExecIter and asyncRegexpSplitIter now coerce sync iterables to async iterables, matching behavior of all other async methods.
  • regexpExec now ensures its RegExp parameter is global unless it is already sticky.
  • slice and asyncSlice now support negative start and end.

Deprecated

  • iter, asyncIter

Added

Methods

  • iterable, asyncIterable (NOTE: These methods are NOT the same as iter and asyncIter. What they do is exactly the opposite!)
  • some, asyncSome
  • every, asyncEvery
  • tap, asyncTap
  • first, asyncFirst,
  • takeSorted, asyncTakeSorted
  • toArray, asyncToArray
  • asyncBuffer

Aliases

  • concat and asyncConcat for chain and asyncChain
  • zipAll and asyncZipAll for zipLongest and asyncZipLongest

Arguments

  • [optional] initial for reduce and asyncReduce. If no initial value is specified, the first item will be used.

[5.0.0] - 2018-06-20

Added

Methods

  • keys, values, entries
  • size
  • find

Changed

  • The behavior of iter(iter(iterable)) should now be considered undefined.
  • asyncZip and asyncZipLongest now run items in parallel

Fixed

  • iter({next: 'foo'}) now returns Iterator[[next, 'foo']] not {next: 'foo'}. This is particular to objects having any property named next.

[4.1.0] - 2018-06-12

Added

Methods

  • consume, asyncConsume
  • batch, asyncBatch, asyncMapBatch
  • regexpExecIter, asyncRegexpExecIter
  • asyncThrottle

[4.0.0] - 2018-06-02

Changed

  • Changed the whole way the library is imported. Very breaking.

Find earlier history on GitHub