Functional Programming eXtensions for JavaScript. Lightweight replacement for Lodash.


Keywords
functional, fp, lodash, functional-programming, toolbox, toolkit, utility, utils
License
Unlicense
Install
npm install fpx@0.12.3

Documentation

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.
  • 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

In browsers and Deno, import by URL:

import * as f from 'https://cdn.jsdelivr.net/npm/fpx@0.12.3/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 both Object.prototype.toString and Array.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), returns val 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:

  • #Nil''.
  • #Scalar → default JS stringification.
  • All other inputs → TypeError exception.

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 [] → use JSON.stringify.
    • Otherwise [object <name>], prioritizing constructor name over Symbol.toStringTag.
      • Exact opposite of default behavior for Object.prototype.toString.
  • 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.
  • 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.
  • 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.
  • 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:

  • Uses #is rather than ===, therefore able to detect NaN.
  • Input may be #nil or any #list.

function includes

Links: source; test/example.

Like Array.prototype.includes. Differences:

  • Supports arbitrary iterables compatible with #values.
  • Iterable may be #nil, equivalent to [].

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 #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.
    • #Maps and #structs are treated as collections of their values rather than key-value entries.

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