fpx
v0.12.3
Published
Functional Programming eXtensions for JavaScript. Lightweight replacement for Lodash.
Downloads
50
Readme
Overview
fpx
: Functional Programming eXtensions for JavaScript. Lightweight replacement for Lodash. Stuff that should be built into the language.
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
- Boolean tests for common types and interfaces.
- Minifiable type assertions.
- Type conversions.
- Tuned for performance.
- Small and dependency-free. Single file. Native JS module.
Differences from Lodash:
- Supports arbitrary iterables and iterators, including sets and maps.
- Type assertions and conversions.
- Much smaller and simpler.
TOC
- #Usage
- #Why
- #Perf
- #API
- #
function bind
- #
function not
- #
function is
- #
function truthy
- #
function falsy
- #
function isNil
- #
function isSome
- #
function isBool
- #
function isNum
- #
function isFin
- #
function isFinNeg
- #
function isFinPos
- #
function isInt
- #
function isNat
- #
function isIntNeg
- #
function isIntPos
- #
function isNaN
- #
function isInf
- #
function isBigInt
- #
function isStr
- #
function isSym
- #
function isKey
- #
function isJunk
- #
function isComp
- #
function isPrim
- #
function isFun
- #
function isFunSync
- #
function isFunGen
- #
function isFunAsync
- #
function isFunAsyncGen
- #
function isObj
- #
function isStruct
- #
function isArr
- #
function isReg
- #
function isDate
- #
function isValidDate
- #
function isInvalidDate
- #
function isSet
- #
function isMap
- #
function isPromise
- #
function isIter
- #
function isIterAsync
- #
function isIterator
- #
function isIteratorAsync
- #
function isGen
- #
function isCls
- #
function isDict
- #
function isList
- #
function isSeq
- #
function isVac
- #
function isScalar
- #
function isInst
- #
function isListOf
- #
function isEmpty
- #
function hasMeth
- #
function req
- #
function opt
- #
function reqInst
- #
function optInst
- #
function only
- #
function arrOf
- #
function prim
- #
function bool
- #
function num
- #
function fin
- #
function int
- #
function nat
- #
function intPos
- #
function str
- #
function dict
- #
function struct
- #
function scalar
- #
function inst
- #
function add
- #
function sub
- #
function mul
- #
function div
- #
function rem
- #
function lt
- #
function gt
- #
function lte
- #
function gte
- #
function neg
- #
function inc
- #
function dec
- #
function nop
- #
function True
- #
function False
- #
function id
- #
function di
- #
function val
- #
function panic
- #
function render
- #
function show
- #
function npo
- #
function hasOwn
- #
function hasOwnEnum
- #
function mut
- #
function mapDict
- #
function pick
- #
function omit
- #
function pickKeys
- #
function omitKeys
- #
function more
- #
function alloc
- #
function arr
- #
function arrCopy
- #
function slice
- #
function keys
- #
function values
- #
function valuesCopy
- #
function entries
- #
function reify
- #
function vac
- #
function indexOf
- #
function includes
- #
function concat
- #
function append
- #
function prepend
- #
function len
- #
function hasLen
- #
function each
- #
function map
- #
function mapMut
- #
function mapCompact
- #
function filter
- #
function reject
- #
function compact
- #
function remove
- #
function fold
- #
function find
- #
function procure
- #
function every
- #
function some
- #
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 mapFrom
- #
function range
- #
function span
- #
function times
- #
function repeat
- #
function set
- #
function setCopy
- #
- #License
- #Misc
Usage
In browsers and Deno, import by URL:
import * as f from 'https://cdn.jsdelivr.net/npm/[email protected]/fpx.mjs'
When using Node or NPM-oriented bundlers like Esbuild:
npm i -E fpx
import * as f from 'fpx'
import * as f from './node_modules/fpx/fpx.mjs'
Why
- Built-ins are insufficient.
- Other libraries are too large.
- Other libraries are annoying to use.
- Other libraries lack vital tools.
Simplicity
Programs must be written for people to read, and only incidentally for machines to execute.
— Abelson & Sussman, "Structure and Interpretation of Computer Programs"
I believe that all code should strive to be simple and educational. This gives me a massive distaste for most code.
Fpx is tuned for brevity, readability, clarity in addition to performance. If you want to understand how this kind of library works, how higher-order functions work, how to manipulate JS data structures, Fpx should hopefully provide a good read.
Assertions
Assertions go a long way in debugging. Fail fast, catch bugs early. In asynchronous code, validating inputs as early as possible, instead of letting it fail mysteriously later, can save you hours of debugging.
Here's the traditional way of doing assertions:
function someHigherOrderFunction(fun) {
if (typeof fun !== 'function') {
throw TypeError(`expected a function, got ${fun}`)
}
// Actual code after the assertion.
}
someHigherOrderFunction({one: 10})
// uncaught TypeError: expected a function, got [object Object]
Annoying to type and really bad for minification. Some folks strip assertions from production builds, but I find the idea flawed. Even in production, failing fast is better than failing mysteriously, and assertions help with debugging when it inevitably fails.
Fpx provides a better alternative:
function someHigherOrderFunction(fun) {
f.req(fun, f.isFun)
// Actual code after the assertion.
}
someHigherOrderFunction({one: 10})
// uncaught TypeError: expected {"one":10} to satisfy test isFun
Much better. Easy to type with editor autocompletion, produces good error messages, and minifies really well. In a minified build, the function name will be mangled, which is good for bundle size. The mangled name is a non-issue with a source map, which you need for debugging anyway.
To support this style of coding, Fpx provides #req
and a bevy of boolean tests.
Perf
Fpx is 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, Fpx's benchmark suite is limited and checked only in V8. When in doubt, measure in your particular environment.
API
Also see changelog: changelog.md.
function bind
Links: source; test/example.
Like Function.prototype.bind
, but instead of taking this
as an argument, takes it contextually. By default this
is undefined
. To set it, use f.bind.call
.
Returns a new function that represents partial application of the given function, a common tool in functional programming. When called, it joins arguments from both calls and invokes the original function. Think of it like splitting a function call in two, or more. Performance is inferior to closures; avoid in hotspots.
const inc = f.bind(f.add, 1)
inc(2)
// 3
Note: Fpx no longer provides facilities for currying. Experience has shown it to be extremely error prone. Currying, as seen in purely functional languages such as Haskell, tends to care about the amount of arguments. Calling a curried function may either create a new function, or call the underlying function (possibly side-effectful). This approach works reasonably well in statically typed languages, but not in JS where all functions are variadic and it's conventional to sometimes pass extra utility arguments "just in case", which the callee may or may not care about. bind
is different because the created function will always call the original function, regardless of how many arguments were passed.
function not
Links: source; test/example.
Returns a new function that negates the result of the given function, like a delayed !
.
function eq(a, b) {return a === b}
const different = f.not(eq)
different(10, 20)
// !eq(10, 20) = true
// equivalent:
function different(a, b) {return !eq(a, b)}
function is
Links: source; test/example.
Identity test: same as ===
, but considers NaN
equal to NaN
. Equivalent to SameValueZero as defined by the language spec. Used internally in Fpx for all identity tests.
Note that Object.is
implements SameValue, which treats -0
and +0
as distinct values. This is typically undesirable. As a result, you should prefer f.is
over ===
or Object.is
.
f.is(1, '1')
// false
f.is(NaN, NaN)
// true
function truthy
Links: source; test/example.
Same as !!
or Boolean
. Sometimes useful with higher-order functions.
function falsy
Links: source; test/example.
Same as !
. Sometimes useful with higher-order functions.
function isNil
Links: source; test/example.
True for null
and undefined
. Same as value == null
. Incidentally, these are the only values that produce an exception when attempting to read a property: null.someProperty
.
// Definition
function isNil(value) {return value == null}
f.isNil(null)
// true
f.isNil(undefined)
// true
f.isNil(false)
// false
function isSome
Links: source; test/example.
Inverse of #isNil
. False for null
and undefined
, true for other values.
function isBool
Links: source; test/example.
Same as typeof val === 'boolean'
.
function isNum
Links: source; test/example.
Same as typeof val === 'number'
. True if the value is a primitive number, including NaN
and ±Infinity
. In most cases you should use isFin
instead.
f.isNum(1)
// true
f.isNum('1')
// false
f.isNum(NaN)
// true <-- WTF
function isFin
Links: source; test/example.
Same as ES2015's Number.isFinite
. True if val
is a primitive number and is not NaN
or ±Infinity
. In most cases you should prefer isFin
over isNum
.
f.isFin(1)
// true
f.isFin('1')
// false
f.isFin(NaN)
// false
function isFinNeg
Links: source; test/example.
True if value is finite (via #isFin
) and < 0.
function isFinPos
Links: source; test/example.
True if value is finite (via #isFin
) and > 0.
function isInt
Links: source; test/example.
True if value is an integer: finite via #isFin
, without a fractional part.
function isNat
Links: source; test/example.
True if value is a natural number: integer >= 0. Also see #isIntPos
.
function isIntNeg
Links: source; test/example.
True if value is integer < 0. Also see #isFinNeg
.
function isIntPos
Links: source; test/example.
True if value is integer > 0. Also see #isNat
, #isFinPos
.
function isNaN
Links: source; test/example.
Same as ES2015's Number.isNaN
. True if value is actually NaN
. Doesn't coerce non-numbers to numbers, unlike global isNaN
.
function isInf
Links: source; test/example.
True if value is -Infinity
or Infinity
.
function isBigInt
Links: source; test/example.
True if value is a primitive BigInt
. False for all other inputs, including BigInt
object wrappers.
function isStr
Links: source; test/example.
Same as typeof val === 'string'
. True if value is a primitive string.
function isSym
Links: source; test/example.
Same as typeof val === 'symbol'
. True if value is a primitive symbol.
function isKey
Links: source; test/example.
True if value qualifies as a dictionary key. True for all primitives excluding garbage values via #isJunk
.
function isJunk
Links: source; test/example.
True for garbage values: #nil, #NaN
, #±Infinity
.
function isComp
Links: source; test/example.
True if value is "composite" / "compound" / "complex". Opposite of #isPrim
. Definition:
function isComp(val) {return isObj(val) || isFun(val)}
function isPrim
Links: source; test/example.
True if value is a JS primitive: not an object, not a function. Opposite of #isComp
.
function isFun
Links: source; test/example.
Same as typeof val === 'function'
. True if value is any function, regardless of its type (arrow, async, generator, etc.).
function isFunSync
Links: source; test/example.
True if the input is a normal sync function. False for generator functions or async functions.
function isFunGen
Links: source; test/example.
True if the input is a sync generator function. False for normal sync functions and async functions.
function isFunAsync
Links: source; test/example.
True if the input is an async non-generator function. False for sync functions, generator functions, or async generator functions.
function isFunAsyncGen
Links: source; test/example.
True if the input is an async generator function. False for sync functions and async non-generator functions.
function isObj
Links: source; test/example.
Same as typeof val === 'object' && val !== null
. True for any JS object: plain dict, array, various other classes. Doesn't include functions, even though JS functions are extensible objects.
Note: this is not equivalent to Lodash's _.isObject
, which counts functions as objects. Use #isComp
for that.
For plain objects used as dictionaries, see #isDict
. For fancy non-list objects, see #isStruct
.
function isStruct
Links: source; test/example.
True if value is a non-iterable object. Excludes both #sync iterables and #async iterables. Note that #dicts are automatically structs, but not all structs are dicts.
function isArr
Links: source; test/example.
Alias for Array.isArray
. Used internally for all array checks.
True if value is an instance of Array
or its subclass. False for all other values, including non-array objects whose prototype is an array.
function isReg
Links: source; test/example.
True if value is an instance of RegExp
or its subclass.
function isDate
Links: source; test/example.
True of value is an instance of Date
. Most of the time you should prefer #isValidDate
.
function isValidDate
Links: source; test/example.
True of value is an instance of Date
and its timestamp is #finite rather than NaN
.
function isInvalidDate
Links: source; test/example.
True of value is an instance of Date
representing an invalid date whose timestamp is NaN
.
function isSet
Links: source; test/example.
True if value is an instance of Set
or its subclass.
function isMap
Links: source; test/example.
True if value is an instance of Map
or its subclass.
function isPromise
Links: source; test/example.
True if the value satisfies the ES2015 promise interface.
function isIter
Links: source; test/example.
True if the value satisfies the ES2015 sync iterable interface. For iterator rather than iterable, use #isIterator
.
function isIterAsync
Links: source; test/example.
True if the value satisfies the ES2015 async iterable interface. For iterator rather than iterable, use #isIteratorAsync
.
function isIterator
Links: source; test/example.
True if the value satisfies the ES2015 sync iterator interface. For iterable rather than iterator, use #isIter
.
function isIteratorAsync
Links: source; test/example.
True if the value satisfies the ES2015 async iterator interface. For iterable rather than iterator, use #isIterAsync
.
function isGen
Links: source; test/example.
True if value is a #sync iterator created by calling a generator function.
function isCls
Links: source; test/example.
True if the input is a function with a prototype, likely to be a class. False for arrow functions such as () => {}
, which don't have a prototype.
function isDict
Links: source; test/example.
True for a "plain object" created via {...}
or Object.create(null)
. False for any other input, including instances of any class other than Object
. Roughly equivalent to Lodash's _.isPlainObject
.
See #isStruct
for a more general definition of a non-iterable object.
function isList
Links: source; test/example.
True for any array-like such as: []
, arguments
, TypedArray
, NodeList
, etc. Used internally for most list checks. Note that primitive strings are not considered lists.
function isSeq
Links: source; test/example.
True for any of:
Many Fpx functions support arbitrary data structures compatible with #values
, but some functions such as #arr
allow only sequences, for sanity checking.
function isVac
Links: source; test/example.
Short for "is vacuous" or "is vacated". Could also be called "is falsy deep". True if the input is #falsy or a #list where all values are vacuous, recursively. Does not iterate non-lists. Also see complementary function #vac
.
function isScalar
Links: source; test/example.
True for a value that could be considered a single scalar, rather than a collection / data structure:
- Any #primitive.
- Any #object with a custom
.toString
method, distinct from bothObject.prototype.toString
andArray.prototype.toString
.
function isInst
Links: source; test/example.
Signature: (val, Cls) => bool
.
Same as instanceof
but does not implicitly convert the operand to an object. True only if the operand is already an instance of the given class.
function isListOf
Links: source; test/example.
Shortcut for isList(val) && every(val, fun)
. True if the input is a list of values that satisfy the given predicate function.
function isEmpty
Links: source; test/example.
True if the input is an empty collection such as list, set, map, or a primitive such as null
. False for any other non-primitive. Treating primitives as "empty" is consistent with other Fpx functions that operate on collections.
function hasMeth
Links: source; test/example.
True if the the given value has the given named method. Safe to call on primitives such as null
. Always false for primitives.
function req
Links: source; test/example.
Signature: (val, test) => val
where test: val => bool
.
Short for "require". Minification-friendly assertion. If !test(val)
, throws an informative TypeError
. Otherwise, returns val
as-is.
f.req({one: `two`}, f.isObj)
// {one: `two`}
f.req('str', f.isFun)
// uncaught TypeError: expected "str" to satisfy test isFun
function opt
Links: source; test/example.
Short for "optional". If val
is #non-nil, uses #req
to validate it. Returns val
as-is.
function reqInst
Links: source; test/example.
Signature: (val, Cls) => val
.
Short for "require instance". Asserts that val
is an instance of the given class. Returns val
as-is.
function optInst
Links: source; test/example.
Short for "optional instance". If val
is #non-nil, uses #reqInst
to validate it. Returns val
as-is.
function only
Links: source; test/example.
Signature: (val, test) => val
where test: val => bool
.
Type filtering utility. If val
satisfies the given test function, returns val
as-is. Otherwise returns undefined
.
function arrOf
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.
function prim
Links: source; test/example.
Shortcut for asserting that the input is a primitive. Throws for non-primitive inputs. Returns the input as-is.
function bool
Links: source; test/example.
Similar to val ?? false
but val
must be #nil or a #boolean, otherwise throws.
function num
Links: source; test/example.
Similar to val ?? 0
but val
must be #nil or a #number, otherwise throws.
function fin
Links: source; test/example.
Similar to val ?? 0
but val
must be #nil or a #finite number, otherwise throws.
function int
Links: source; test/example.
Similar to val ?? 0
but val
must be #nil or an #integer, otherwise throws.
function nat
Links: source; test/example.
Similar to val ?? 0
but val
must be #nil or a #natural number, otherwise throws.
function intPos
Links: source; test/example.
Similar to val ?? 0
but val
must be #nil or a #positive integer, otherwise throws.
function str
Links: source; test/example.
Similar to val ?? ''
but val
must be #nil or a #string, otherwise throws.
function dict
Links: source; test/example.
Similar to val ?? Object.create(null)
but val
must be #nil or a #dict, otherwise throws.
function struct
Links: source; test/example.
Similar to val ?? Object.create(null)
but val
must be #nil or a #struct, otherwise throws.
Most Fpx functions that operate on data structures, such as #filter
, support structs, treating them similarly to maps. A struct is considered a collection of its #values
. Iterating over #keys
or #entries
is opt-in.
function scalar
Links: source; test/example.
Type checking shortcut. Asserts that the input #is scalar, returning it as-is.
function inst
Links: source; test/example.
Signature: (any, typeof A) => A
.
Idempotently converts an arbitrary input to a given class:
- If
isInst(val, cls)
, returnsval
as-is. - Otherwise returns
new cls(val)
.
const newInst = f.inst([10, 20, 30], Set)
// Set{10, 20, 30}
const oldInst = f.inst(newInst, Set)
// Set{10, 20, 30}
newInst === oldInst
// true
function add
Links: source; test/example.
Same as +
.
function sub
Links: source; test/example.
Same as -
.
function mul
Links: source; test/example.
Same as *
.
function div
Links: source; test/example.
Same as /
.
function rem
Links: source; test/example.
Same as %
.
function lt
Links: source; test/example.
Same as <
.
function gt
Links: source; test/example.
Same as >
.
function lte
Links: source; test/example.
Same as <=
.
function gte
Links: source; test/example.
Same as >=
.
function neg
Links: source; test/example.
Arithmetic negation. Same as unary -
.
function inc
Links: source; test/example.
Increments by 1
.
function dec
Links: source; test/example.
Decrements by 1
.
function nop
Links: source; test/example.
Empty function. Functional equivalent of ;
or undefined
. Sometimes useful with higher-order functions.
function True
Links: source; test/example.
Always returns true
. Sometimes useful with higher order functions.
function False
Links: source; test/example.
Always returns false
. Sometimes useful with higher order functions.
function id
Links: source; test/example.
Identity function: returns its first argument unchanged. Sometimes useful with higher-order functions.
function di
Links: source; test/example.
Returns its second argument unchanged. Sometimes useful with higher-order functions.
function val
Links: source; test/example.
Takes a value and creates a function that always returns that value. Sometimes useful with higher order functions.
const constant = f.val(1)
constant()
// 1
constant(`this input is ignored`)
// 1
function panic
Links: source; test/example.
Same as throw
but an expression rather than a statement. Also sometimes useful with higher-order functions.
const x = someTest ? someValue : f.panic(Error(`unreachable`))
function render
Links: source; test/example.
Renders a value for user display. Counterpart to #show
, which renders a value for debug purposes. Intended only for #scalar values. Rules:
function show
Links: source; test/example.
Renders a value for debug purposes. Counterpart to #render
, which renders a value for user display. Convenient for interpolating things into error messages. Used internally in assertion functions such as #req
. Approximate rules:
- String → use
JSON.stringify
. - Function →
[function ${val.name || val}]
.- For named functions, this shorter representation is usually preferable to printing the entire source code.
- Object →
- Plain
{}
or[]
→ useJSON.stringify
. - Otherwise
[object <name>]
, prioritizing constructor name overSymbol.toStringTag
.- Exact opposite of default behavior for
Object.prototype.toString
.
- Exact opposite of default behavior for
- Plain
- Otherwise → default JS stringification.
function npo
Links: source; test/example.
Short for "null-prototype object". Syntactic shortcut for Object.create(null)
.
function hasOwn
Links: source; test/example.
Same as Object.prototype.hasOwnProperty
but shorter and safe to call on primitives. Always false for primitives.
function hasOwnEnum
Links: source; test/example.
Same as Object.prototype.propertyIsEnumerable
but shorter and safe to call on primitives. Always false for primitives.
function mut
Links: source; test/example.
Signature: (tar, src) => tar
.
Similar to Object.assign
. Differences:
- Supports only one source argument.
- Much faster.
- Much safer:
- Target must be a #struct. Throws if target is a function or iterable.
- Source must be nil or a struct. Throws if source is an iterable, non-nil primitive, etc.
- Does not override inherited properties.
- Does not override own non-enumerable properties.
function mapDict
Links: source; test/example.
Signature: ({[Key: A]}, A => B) => {[Key: B]}
.
Similar to #map
but for dicts. Creates a version of the given dict where values have been replaced by calling the given function for each value. Returns an empty dict if the input is #nil.
function pick
Links: source; test/example.
Signature: ({[Key: A]}, A => bool) => {[Key: A]}
.
Similar to #filter
but for dicts. Returns a version of the given dict with only the properties for which fun
returned something truthy. Returns an empty dict if the input is #nil.
function omit
Links: source; test/example.
Signature: ({[Key: A]}, A => bool) => {[Key: A]}
.
Similar to #reject
but for dicts. Returns a version of the given dict without properties for which fun
returned something truthy. Returns an empty dict if the input is #nil.
function pickKeys
Links: source; test/example.
Signature: ({[Key: A]}, keys) => {[Key: A]}
.
Returns a version of the given dict, keeping only the given properties. Keys can be either a Set
or an arbitrary #sequence. Each key must satisfy #isKey
. Existence is not required: missing properties are silently ignored. Returns an empty dict if the input is #nil.
function omitKeys
Links: source; test/example.
Signature: ({[Key: A]}, keys) => {[Key: A]}
.
Returns a version of the given dict without the given properties. Keys must be an arbitrary sequence convertible to a Set
. Returns an empty dict if the input is #nil.
function more
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
.
function alloc
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
.
function arr
Links: source; test/example.
Converts an arbitrary #sequence to an array. Allows 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
, arr
rejects other inputs such as non-nil primitives, dicts, maps, arbitrary iterables, ensuring that the input is always a sequence.
function arrCopy
Links: source; test/example.
Like #arr
, converts an arbitrary sequence to an array. Unlike arr,
always makes a copy. Mutating the output doesn't affect the original.
function slice
Links: source; test/example.
Like Array.prototype.slice
but allows arbitrary #sequences compatible with #arr
.
function keys
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
f.span(f.len(iterator))
. See #span
and #len
. - For #structs: equivalent to
Object.keys
.
function values
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
.
function valuesCopy
Links: source; test/example.
Variant of #values
that always makes a copy. Mutating the output doesn't affect the original.
function entries
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
.
function reify
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.
function vac
Links: source; test/example.
Complements #isVac
. Returns undefined
if the input is vacuous, otherwise returns the input as-is.
function indexOf
Links: source; test/example.
Like Array.prototype.indexOf
. Differences:
function includes
Links: source; test/example.
Like Array.prototype.includes
. Differences:
function concat
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
.
function append
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and appends an arbitrary value, returning the resulting array.
function prepend
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and prepends an arbitrary value, returning the resulting array.
function len
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
.
function hasLen
Links: source; test/example.
Shortcut for #len
> 0.
function each
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.
function map
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
or additional arguments.
function mapMut
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
.
function mapCompact
Links: source; test/example.
Equivalent to f.compact(f.map(val, fun))
. See #map
and #compact
.
function filter
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.
function reject
Links: source; test/example.
Opposite of #filter
. Equivalent to f.filter(val, f.not(fun))
.
function compact
Links: source; test/example.
Equivalent to f.filter(val, f.id)
. Takes an arbitrary iterable and returns an array of its truthy #values
, discarding falsy values.
function remove
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
.
function fold
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.
function find
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.
function procure
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 f.find(f.map(src, fun), f.id)
but more efficient.
function every
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.
function some
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.
function head
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and returns its first element or undefined
.
function last
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and returns its last element or undefined
.
function init
Links: source; test/example.
Short for "initial". Takes an arbitrary iterable compatible with #values
and returns an array of all its values except last.
function tail
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and returns an array of all its values except first.
function take
Links: source; test/example.
Takes an arbitrary iterable compatible with #values
and returns N values from the start.
function count
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.
function compare
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.
function compareFin
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.
function sort
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.
function reverse
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.
function index
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 keyed 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.
Similar to Lodash's _.keyBy
. Compare #group
which keeps all values for each group, rather than only the last.
function group
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.
function partition
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.
function sum
Links: source; test/example.
Signature: (Iter<A>) => fin
.
Sums all finite #values
of an arbitrary iterable, ignoring all non-finite values.
function zip
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.
function mapFrom
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.
function range
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
.
function span
Links: source; test/example.
Signature: nat => nat[]
.
Returns an array of the given length, where values are integers from 0. Shortcut for f.range(0, length)
. Nil length is equivalent to 0.
function times
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 f.mapMut(f.span(len), fun)
.
function repeat
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 f.alloc(len).fill(val)
.
function set
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
.
function setCopy
Links: source; test/example.
Similar to #set
: 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.
License
https://unlicense.org
Misc
I'm receptive to suggestions. If this library almost satisfies you but needs changes, open an issue or chat me up. Contacts: https://mitranim.com/#contacts