iter.mjs provides tiny utils for iteration and functional programming. Lightweight replacement for Lodash. Features:
- Higher-order functions for data structures.
- Common FP tools like
map
,filter
, and many more. - Compatible with arbitrary iterables such as lists, sets, maps, dicts.
- Common FP tools like
Differences from Lodash:
- Supports arbitrary iterables and iterators, including sets and maps.
- Much smaller and simpler.
Port and rework of https://github.com/mitranim/fpx.
- #Usage
- #Perf
- #API
- #
function arrOf
- #
function more
- #
function alloc
- #
function arr
- #
function arrCopy
- #
function slice
- #
function keys
- #
function values
- #
function valuesCopy
- #
function entries
- #
function reify
- #
function indexOf
- #
function findIndex
- #
function includes
- #
function append
- #
function prepend
- #
function concat
- #
function len
- #
function hasLen
- #
function each
- #
function map
- #
function mapMut
- #
function mapCls
- #
function mapCompact
- #
function mapFlat
- #
function filter
- #
function reject
- #
function compact
- #
function remove
- #
function fold
- #
function fold1
- #
function find
- #
function procure
- #
function every
- #
function some
- #
function flat
- #
function head
- #
function last
- #
function init
- #
function tail
- #
function take
- #
function count
- #
function compare
- #
function compareFin
- #
function sort
- #
function reverse
- #
function index
- #
function group
- #
function partition
- #
function sum
- #
function zip
- #
function setOf
- #
function setFrom
- #
function setCopy
- #
function mapOf
- #
function range
- #
function span
- #
function times
- #
function repeat
- #
function mapDict
- #
function pick
- #
function omit
- #
function pickKeys
- #
function omitKeys
- #Undocumented
- #
import * as i from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/iter.mjs'
Carefully tuned for performance. Functions covered by benchmarks appear comparable to their native or Lodash equivalents. Many appear significantly faster.
JS performance is complicated and very unstable, Our benchmark suite is limited and checked only in V8. When in doubt, measure in your particular environment.
Links: source; test/example.
Signature: (seq<A>, test) => A[]
where test: A => true
.
Shortcut. Converts the input to an array via #arr
and asserts that every element satisfies the given test function. Returns the resulting array.
Links: source; test/example.
Takes an iterator, consumes one value, and returns true if the iterator is not yet finished. Shortcut for val.next().done === false
.
Links: source; test/example.
Shortcut for allocating an array with a sanity check. Same as Array(N)
but ensures that the input is a natural_number suitable for array length. Avoids unintentionally passing any non-natural input such as Array(-1)
. Allows nil, replacing it with 0
.
Links: source; test/example.
Converts an arbitrary sequence to an array. Supports the following inputs:
- Nil: return
[]
. - Array: return as-is.
- List: convert via
Array.prototype.slice
. - Set or arbitrary iterator: convert to array by iterating.
Unlike #values
, this function rejects other inputs such as non-nil primitives, dicts, maps, arbitrary iterables, ensuring that the input is always a sequence.
The input may or may not be a copy. To ensure copying, use #arrCopy
.
Links: source; test/example.
Similar to #arr
, but always makes a copy, even if the input is already a true array.
Links: source; test/example.
Like Array.prototype.slice
but allows arbitrary sequences compatible with #arr
.
Links: source; test/example.
Takes an arbitrary input and returns an array of its keys:
- For non-objects: always
[]
. - For iterables with
.keys()
: equivalent to converting the output of.keys()
to an array. Implementation varies for performance.- Examples:
Array
,Set
,Map
, and more.
- Examples:
- For lists: equivalent to above for arrays.
- For iterators: exhausts the iterator, returning an array of indexes equivalent to
i.span(i.len(iterator))
. See #span
and #len
. - For structs: equivalent to
Object.keys
.
Links: source; test/example.
Takes an arbitrary input and returns an array of its values:
- For non-objects: always
[]
. - For arrays: returns as-is without copying.
- For lists: slice to array.
- For iterables with
.values()
: equivalent to converting the output of.values()
to an array. Implementation varies for performance.- Examples:
Set
,Map
, and more.
- Examples:
- For iterators: equivalent to
[...iterator]
. - For structs: equivalent to
Object.values
.
Links: source; test/example.
Variant of #values
that always makes a copy. Mutating the output doesn't affect the original.
Links: source; test/example.
Takes an arbitrary input and returns an array of its entries (key-value tuples):
- For non-objects: always
[]
. - For iterables with
.entries()
: equivalent to converting the output of.entries()
to an array. Implementation varies for performance.- Examples:
Set
,Map
, and more.
- Examples:
- For lists: equivalent to above for arrays.
- For iterators: exhausts the iterator, returning an array of entries where keys are indexes starting with 0.
- For structs: equivalent to
Object.entries
.
Links: source; test/example.
Takes an arbitrary value and attempts to deeply materialize it. Any iterators, or lists that contain iterators, or lists that contain lists that contain iterators, etc., are converted to arrays. Does not inspect other data structures such as sets or dicts.
Links: source; test/example.
Like Array.prototype.indexOf
. Differences:
Links: source; test/example.
Signature: (List<A>, A => bool) => int
.
Like Array.prototype.findIndex
. Differences:
Links: source; test/example.
Like Array.prototype.includes
. Differences:
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and appends an arbitrary value, returning the resulting array.
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and prepends an arbitrary value, returning the resulting array.
Links: source; test/example.
Like Array.prototype.concat
. Differences:
- Takes two arguments, without rest/spread.
- Supports arbitrary iterables compatible with #
values
. - Iterables may be nil, equivalent to
[]
.
Note: for individual elements, use #append
and
#prepend
.
Links: source; test/example.
Universal length measurement:
- For non-objects: always 0.
- For iterables:
- For lists: same as
.length
. - For ES2015 collections such as
Set
: same as.size
. - For iterators: exhausts the iterator, returning element count.
- For lists: same as
- For structs: equivalent to
Object.keys(val).length
.
Links: source; test/example.
Shortcut for #len
> 0.
Links: source; test/example.
Signature: (Iter<A>, A => void) => void
.
Similar to Array.prototype.forEach
, Set.prototype.forEach
, Map.prototype.forEach
, and so on. Differences:
- Takes an arbitrary iterable compatible with #
values
. - Iterable may be nil, equivalent to
[]
. - Doesn't support
this
or additional arguments.
Links: source; test/example.
Signature: (Iter<A>, A => B) => B[]
.
Similar to Array.prototype.map
. Differences:
- Takes an arbitrary iterable compatible with #
values
. - Iterable may be nil, equivalent to
[]
. - Doesn't support
this
, and doesn't pass additional arguments. When you want support for additional arguments, use #values
to convert an arbitrary iterable to an array, then use native.map
.
Links: source; test/example.
Similar to Array.prototype.map
. Differences:
- Mutates the input (which must be an array).
- Doesn't support
this
or additional arguments.
For a non-mutating version, see #map
.
Links: source; test/example.
Signature: (Iter<A>, {new(A): B}) => B[]
.
Similar to #map
, but instead of taking an arbitrary function, takes a class and calls it with new
for each element.
import * as i from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/iter.mjs'
import * as o from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/obj.mjs'
class Model extends o.Dict {pk() {return this.id}}
class Person extends Model {}
console.log(i.mapCls(
[
{id: 1, name: `Mira`},
{id: 2, name: `Kara`},
],
Person,
))
/*
[
Person { id: 1, name: "Mira" },
Person { id: 2, name: "Kara" },
]
*/
Links: source; test/example.
Equivalent to i.compact(i.map(val, fun))
. See #map
and #compact
.
Links: source; test/example.
Signature: (Iter<A>, A => B[]) => B[]
.
Similar to Array.prototype.flatMap
. Differences:
- Takes an arbitrary iterable compatible with #
values
. - Iterable may be nil, equivalent to
[]
. - Doesn't support
this
or additional arguments.
This function is equivalent to i.flat(i.map(val, fun))
. See #map
and #flat
.
Links: source; test/example.
Signature: (Iter<A>, A => bool) => A[]
.
Similar to Array.prototype.filter
. Differences:
- Takes an arbitrary iterable compatible with #
values
. - Iterable may be nil, equivalent to
[]
. - Doesn't support
this
or additional arguments.
Links: source; test/example.
Opposite of #filter
. Equivalent to i.filter(val, l.not(fun))
.
Links: source; test/example.
Equivalent to i.filter(val, l.id)
. Takes an arbitrary iterable and returns an array of its truthy #values
, discarding falsy values.
Links: source; test/example.
Signature: (Iter<A>, A) => A[]
.
Takes an arbitrary iterable and an element to remove. Returns an array of the iterable's #values
, discarding each occurrence of this element, comparing via is
.
Links: source; test/example.
Signature: (src: Iter<A>, acc: B, fun: (B, A) => B) => B
.
Similar to Array.prototype.reduce
. Differences:
- Takes an arbitrary iterable compatible with #
values
. - Iterable may be nil, equivalent to
[]
. - Arguments are
(src, acc, fun)
rather than(fun, acc)
. - Accumulator argument is mandatory.
- Doesn't support
this
. - Iterator function receives exactly two arguments: accumulator and next value.
Links: source; test/example.
Signature: (src: Iter<A>, fun: (A, A) => A) => A
.
Similar to #fold
but instead of taking an accumulator argument, uses the first element of the iterable as the initial accumulator value. If the iterable is empty, returns undefined
.
Similar to Array.prototype.reduce
when invoked without an accumulator argument.
Links: source; test/example.
Signature: (Iter<A>, A => bool) => A
.
Similar to Array.prototype.find
. Differences:
- Takes an arbitrary iterable compatible with #
values
. - Iterable may be nil, equivalent to
[]
. - Doesn't support
this
or additional arguments.
Links: source; test/example.
Signature: (src: Iter<A>, fun: A => B) => B
.
Similar to #find
, but returns the first truthy result of calling the iterator function, rather than the corresponding element. Equivalent to i.find(i.map(src, fun), l.id)
but more efficient.
Links: source; test/example.
Signature: (Iter<A>, A => bool) => bool
.
Similar to Array.prototype.every
. Differences:
- Takes an arbitrary iterable compatible with #
values
. - Iterable may be nil, equivalent to
[]
. - Doesn't support
this
or additional arguments.
Links: source; test/example.
Signature: (Iter<A>, A => bool) => bool
.
Similar to Array.prototype.some
. Differences:
- Takes an arbitrary iterable compatible with #
values
. - Iterable may be nil, equivalent to
[]
. - Doesn't support
this
or additional arguments.
Links: source; test/example.
Similar to Array.prototype.flat
. Differences:
- Takes an arbitrary iterable compatible with #
values
. - Always flattens to infinite depth.
Currently flattens only children and descendants that are plain, preserving other nested iterables as-is.
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and returns its first element or undefined
.
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and returns its last element or undefined
.
Links: source; test/example.
Short for "initial". Takes an arbitrary iterable compatible with #values
and returns an array of all its values except last.
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and returns an array of all its values except first.
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and returns N values from the start.
Links: source; test/example.
Signature: (src: Iter<A>, fun: A => B) => nat
.
Takes an arbitrary iterable compatible with #values
, calls the given function for each value, and returns the count of truthy results. The count is between 0 and iterable length.
Links: source; test/example.
Signature: (a, b) => -1 | 0 | 1
.
Equivalent to the default JS sort comparison algorithm. Sometimes useful for sorting via Array.prototype.sort
or #sort
, as a fallback.
Links: source; test/example.
Signature: (a, b) => -1 | 0 | 1
where arguments are nil or finite.
Sort comparison for finite numbers. Usable for Array.prototype.sort
or #sort
. Throws on non-nil, non-finite arguments.
Links: source; test/example.
Signature: (src: Iter<A>, fun?: (prev: A, next: A) => -1 | 0 | 1) => A[]
.
Similar to Array.prototype.sort
. Differences:
- Takes an arbitrary iterable compatible with #
values
. - Iterable may be nil, equivalent to
[]
. - Always creates a new array. Does not mutate the input.
The comparison function is optional. If omitted, default JS sorting is used.
Links: source; test/example.
Similar to Array.prototype.reverse
. Differences:
- Takes an arbitrary iterable compatible with #
values
. - Iterable may be nil, equivalent to
[]
. - Always creates a new array. Does not mutate the input.
Links: source; test/example.
Signature: (Iter<A>, A => Key | any) => {[Key: A]}
.
Takes an arbitrary iterable compatible with #values
and returns an index where its values are indexed by the given function, hence the name. The function is called for each value. If the function returns a valid_key, the key-value pair is added to the index. Invalid keys are ignored. If the function returns the same key for multiple values, previous values are lost.
Compare #group
which keeps all values for each group, rather than only the last.
Links: source; test/example.
Signature: (Iter<A>, A => Key | any) => {[Key: A[]]}
.
Takes an arbitrary iterable compatible with #values
and groups its values by keys generated by the given function. The function is called for each value. If the function returns a valid_key, the value is added to the index under that key. Invalid keys are ignored.
Compare #index
, which keeps only the last value for each group.
Links: source; test/example.
Signature: (Iter<A>, A => bool) => [A[], A[]]
.
Partitions the #values
of a given iterable, returning a tuple of two groups: values that satisfy the predicate and the remainder.
Links: source; test/example.
Signature: (Iter<A>) => fin
.
Sums all finite #values
of an arbitrary iterable, ignoring all non-finite values.
Links: source; test/example.
Signature: (Iter<[Key, A]>) => {[Key: A]}
.
Similar to Object.fromEntries
. Differences:
- Takes an arbitrary iterable compatible with #
values
(more flexible).- Each value of this iterable must be a key-value pair.
- Ignores entries where the first element is not a valid_key.
- Returns a null_prototype_object.
- Slightly slower.
Links: source; test/example.
Syntactic shortcut for creating a Set
via variadic call.
import * as i from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/iter.mjs'
i.setOf(10, 20, 30)
// Set{10, 20, 30}
Links: source; test/example.
Converts an arbitrary input to a native Set
. Similar to new Set
. Differences:
- If input is already a set: return as-is without copying.
- Otherwise, create a set of the input's #
values
.
Links: source; test/example.
Similar to #setFrom
: converts an arbitrary input to a set. Difference: always makes a copy. If the original was a set, it's unaffected by mutations of the output.
Links: source; test/example.
Syntactic shortcut for creating a Map
with inline keys and values. Shorter and less noisy than either new Map
with an array of entries or chained .set
calls. The name mirrors Array.of
.
Links: source; test/example.
Signature: (min: int, max: int) => int[]
.
Returns an array of contiguous integers in the range of [min, max)
. The first value is min
, the last value is max - 1
.
Links: source; test/example.
Signature: nat => nat[]
.
Returns an array of the given length, where values are integers from 0. Shortcut for i.range(0, length)
. Nil length is equivalent to 0.
Links: source; test/example.
Signature: (len: nat, fun: nat => A) => A[]
.
Takes an array length and a mapping function. Returns an array of the given length, where each element is the result of calling the given function, passing the element's index, starting with 0. Equivalent to i.mapMut(i.span(len), fun)
.
Links: source; test/example.
Signature: (len: nat, val: A) => A[]
.
Returns an array of the given length where each element is the given value. Equivalent to i.alloc(len).fill(val)
.
Links: source; test/example.
Signature: ({[Key: A]}, A => B) => {[Key: B]}
.
Similar to #map
but for dicts.
- The input must be either nil or a struct. Nil is considered
{}
. - The output is always a plain with the same keys but altered values.
- The mapping function receives only one argument: each value.
import * as i from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/iter.mjs'
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
i.mapDict({one: 10, two: 20}, l.inc)
// {one: 11, two: 21}
Performance note: dictionary iteration is much slower than array iteration, and should be avoided or minimized.
Links: source; test/example.
Signature: ({[Key: A]}, A => bool) => {[Key: A]}
.
Similar to #filter
but for dicts.
- The input must be either nil or a struct. Nil is considered
{}
. - The output is always a plain. It has only the key-values from the original input for which the given function returned a truthy result.
- The mapping function receives each value.
import * as i from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/iter.mjs'
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
i.pick({one: -20, two: -10, three: 10, four: 20}, l.isFinPos)
// {three: 10, four: 20}
Performance note: dictionary iteration is much slower than array iteration, and should be avoided or minimized.
Links: source; test/example.
Signature: ({[Key: A]}, A => bool) => {[Key: A]}
.
Similar to #reject
but for dicts.
- The input must be either nil or a struct. Nil is considered
{}
. - The output is always a plain. It has only the key-values from the original input for which the given function returned a falsy result.
- The mapping function receives each value.
import * as i from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/iter.mjs'
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
i.omit({one: -20, two: -10, three: 10, four: 20}, l.isFinPos)
// {one: -20, two: -10}
Performance note: dictionary iteration is much slower than array iteration, and should be avoided or minimized.
Links: source; test/example.
Signature: ({[Key: A]}, Iter<Key>) => {[Key: A]}
.
Similar to [#
pick](#function-pick)
but uses keys instead of a function.
- The input must be either nil or a struct. Nil is considered
{}
. - The output is always a plain. It mirrors the original, but has only "known" given keys, excluding any other.
Performance note: dictionary iteration is much slower than array iteration, and should be avoided or minimized.
Links: source; test/example.
Signature: ({[Key: A]}, Iter<Key>) => {[Key: A]}
.
Similar to [#
omit](#function-omit)
but uses keys instead of a function.
- The input must be either nil or a struct. Nil is considered
{}
. - The output is always a plain. It mirrors the original, but has only "unknown" keys, excluding any given keys.
Performance note: dictionary iteration is much slower than array iteration, and should be avoided or minimized.
The following APIs are exported but undocumented. Check iter.mjs.