JFL - JavaScript Standard Functional Library

This JavaScript library aims to compliment the language to provide a single dependency for writing most business logic code. It is fully statically typed to work well with both Flow and TypeScript.

Github

Note on types

This library is centered around basic collection types available in JavaScript: Arrays, Maps and Sets. These are all KeyedCollections (Arrays have indices and Sets have the same keys as values). But for simplicity many functions accept values which match the simpler Collection interface, which only declares that there are values.

Additionally the library definition uses the type aliases $Array, $Map and $Set for the read-only versions of Array, Map and Set (these are declared as $ReadOnly... in Flow and ReadOnly... in TypeScript).

(jfl)

This is the main module from which you can easily import all the others, plus a few utilities for working in Flow/TypeScript in general.

import {Ar, $Ar, Cl, Mp, $Mp, Mth, REx, St, $St, Str} from 'jfl'
import {nullthrows, invariant} from 'jfl'

Type

nullthrows

Returns value if it's not null or undefined, throws otherwise.

Useful for coercing the nullable type of value to non-nullable when using Flow.

nullthrows<T>(
  value: ?T,
  message?: string = 'Got unexpected null or undefined',
): T
nullthrows(null) // throws
nullthrows('a') // 'a'

Source Tests

invariant

Throws an error with message if condition is false.

Both Flow and TypeScript will consider the condition to be an assertion and will infer types correspondingly.

invariant(condition: boolean, message: string): void
invariant(typeof 3 === 'string', 'expecting a string') // throws
invariant(typeof 'a' === 'string', 'expecting a string') // no-op

Source Tests

Ar (array)

This module provides functions which operate on collections (Arrays, Maps, Sets) and return read-only (immutable) Arrays.

import {Ar, $Ar} from 'jfl'

Construct

$Ar

Create an Array.

Prefer array literal `[1, 2, 3]`. This function always returns a reference to the same empty array when called with no arguments.

$Ar<V>(...args: $Array<V>): $Array<V>
$Ar(1, 2, 3) // [1, 2, 3]

Source Tests

Ar.from

Convert any collection of values to an Array of values.

Note that this is not a way to clone an Array, if passed an Array, the same array will be returned.

from<V>(collection: Collection<V>): $Array<V>
Ar.from(Set(1, 2, 3)) // [1, 2, 3]
Ar.from(Mp({a: 1, b: 2, c: 3})) // [1, 2, 3]

Source Tests

Ar.fromAsync

Convert any collection of awaitable promises of values to a single promise of an Array of values.

fromAsync<V>(
  collection: Collection<Promise<V>>,
): Promise<$Array<V>>
await Ar.fromAsync([(async () => 1)(), (async () => 2)()]) // [1, 2]

Source Tests

Ar.keys

Convert any collection with keys to an Array of keys.

Notably the keys of a Set are just its values. The keys of an Array are its indices.

keys<K>(collection: KeyedCollection<K, any>): $Array<K>
Ar.keys([5, 6]) // [0, 1]
Ar.keys($Mp({a: 2, b: 3})) // ['a', 'b']
Ar.keys($St(3, 4) // [3, 4]

Source Tests

Ar.entries

Convert any collection with keys to an Array of key value pairs.

Notably the keys of a Set are just its values. The keys of an Array are its indices.

entries<K, V>(
  collection: KeyedCollection<K, V>,
): $Array<[K, V]>
Ar.entries([5, 6]) // [[0, 5], [1, 6]]
Ar.entries($Mp({a: 2, b: 3})) // [['a', 2], ['b', 3]]
Ar.entries($St(3, 4) // [[3, 3], [4, 4]]

Source Tests

Ar.range

Create an Array of numbers.

The start of the range is inclusive, the end is exclusive. By default increments by 1.

range(
  fromInclusive: number,
  toExclusive: number,
  step?: number = 1,
): $Array<number>
Ar.range(1, 6) // [1, 2, 3, 4, 5]
Ar.range(-0.5, 0.51, 0.5) // [-0.5, 0, 0.5]

Source Tests

Ar.rangeInclusive

Create an Array of numbers.

A version of Ar.range where the end is inclusive.

rangeInclusive(
  fromInclusive: number,
  toInclusive: number,
  step?: number = 1,
): $Array<number>
Ar.rangeInclusive(-0.5, 0.5, 0.5) // [-0.5, 0, 0.5]

Source Tests

Ar.rangeDescending

Create an Array of numbers.

A version of Ar.range where the array of numbers has decreasing order. By default increments by -1.

rangeDescending(
  fromInclusive: number,
  toExclusive: number,
  step?: number = 1,
): $Array<number>
Ar.rangeDescending(5, 1) // [5, 4, 3, 2]
Ar.rangeDescending(2, 1, 0.2) // [2, 1.8, 1.6, 1.4, 1.2]

Source Tests

Ar.rangeDynamic

Create an Array of numbers.

A version of Ar.range where the array of numbers has increasing or decreasing order, depending on given range limits. Both limits are inclusive.

rangeDynamic(
  fromInclusive: number,
  toInclusive: number,
  step?: number = 1,
): $Array<number>
Ar.rangeDynamic(2, 6, 2) // [2, 4, 6]
Ar.rangeDynamic(6, 2, 2) // [6, 4, 2]

Source Tests

Ar.repeat

Create an Array filled with a count of given values.

The value will be referenced, not cloned.

repeat<V>(value: V, count: number): $Array<V>
Ar.repeat(4, "a") // ["a", "a", "a", "a"]

Source Tests

Ar.fill

Create an Array filled with count results of calling fn.

fn takeFirst as the first argument the index where the current invocation's result will be placed.

fill<V>(count: number, fn: number => V): $Array<V>
Ar.fill(4, i => i) // [0, 1, 2, 3]

Source Tests

Ar.fillAsync

Create a promise of an Array filled with count results of calling fn.

fn takeFirst as the first argument the index where the current invocation's result will be placed.

fillAsync<V>(
  count: number,
  fn: number => Promise<V>,
): Promise<$Array<V>>
await Ar.fillAsync(4, async i => i) // [0, 1, 2, 3]

Source Tests

Ar.generate

Create an Array using a seed value and a function which given the seed returns an item to be contained in the array and a new seed value.

To mark the end of the array, fn must return null or undefined.

generate<V, S>(seed: S, fn: S => ?[V, S]): $Array<V>
Ar.generate(2, n => (< 64 ? [n, n * n] : null)) // [2, 4, 16]

Source Tests

Ar.mutable

Convert any collection of values to a mutable Array of values.

If an Array is given it will be cloned.

This function is useful for complicated or performance sensitive computation inside a function. Avoid passing arrays as mutable around your codebase to prevent bugs.

mutable<V>(collection: Collection<V>): Array<V>
Ar.mutable($Ar(1, 2, 3)) // [1, 2, 3]

Source Tests

Check

Ar.isArray

Returns whether given value is an Array.

isArray(argument: mixed): bool
Ar.isArray([1, 2, 3]) // true

Source Tests

Ar.equals

Returns whether given Arrays are equal.

All items must be strictly equal.

equals<V>(
  array: $Array<V>,
  ...arrays: $Array<$Array<V>>
): boolean
Ar.equals([1, 2], [1, 2]) // true

Source Tests

Ar.equalsNested

Returns whether given Arrays and any nested collections are equal.

Any contained collections must deeply equal, all other items must be strictly equal.

equalsNested<V>(
  array: $Array<V>,
  ...arrays: $Array<$Array<V>>
): boolean
Ar.equalsNested([[1], [2], 3], [[1], [2], 3]) // true

Source Tests

Select

Ar.filter

Create a new array by filtering out values from `collection for which predicateFn returns false.

filter<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): $Array<V>
Ar.filter([1, 2, 3], n => Mth.isOdd(n)) // [1, 3]

Source Tests

Ar.filterAsync

Create a promise of an Array by filtering out values in collection for which async predicateFn returns false.

Executes predicateFn on all items in collection concurrently.

async function filterAsync<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => Promise<boolean>,
): Promise<$Array<V>>
await Ar.filterAsync([1, 2, 3], async x => Mth.isOdd(x)) // [1, 3]

Source Tests

Ar.filterNulls

Create a new array by filtering out nulls and undefineds from collection.

Here because its type is more specific then the generic filter function.

filterNulls<V>(collection: Collection<?V>): $Array<V>
Ar.filterNulls([1, null, 3]) // [1, 3]

Source Tests

Ar.filterKeys

Create an Array of keys corresponding to values passing given predicateFn.

filterKeys<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: V => boolean,
): $Array<K>
Ar.filterKeys([1, 2, 3], n => Mth.isOdd(n)) // [1, 3]

Source Tests

Ar.unique

Create an Array of values from collection with each value included only once.

unique<V>(collection: Collection<V>): $Array<V>
Ar.unique([1, 2, 1]) // [1]

Source Tests

Ar.uniqueBy

Create an Array of values from collection with each value included only once, where value equivalence is determined by calling identityFn on each value. Later values overwrite previous ones.

uniqueBy<K, V>(
  collection: KeyedCollection<K, V>,
  identityFn: (V, K) => mixed,
): $Array<V>
Ar.uniqueBy([2, 4, 7], n => n % 3) // [2, 7]

Source Tests

Ar.takeFirst

Create an Array containing the first n items of collection.

Throws if n is negative.

takeFirst<V>(collection: Collection<V>, n: number): $Array<V>
Ar.takeFirst([1, 2, 3], 2) // [1, 2]

Source Tests

Ar.dropFirst

Create an Array containing all but the first n items of collection.

Throws if n is negative.

dropFirst<V>(collection: Collection<V>, n: number): $Array<V>
Ar.dropFirst([1, 2, 3], 2) // [3]

Source Tests

Ar.takeLast

Create an Array containing the last n items of collection.

takeLast<V>(collection: Collection<V>, n: number): $Array<V>
Ar.takeLast([1, 2, 3], 2) // [2, 3]

Source Tests

Ar.dropLast

Create an Array containing all but the last n items of collection.

dropLast<V>(collection: Collection<V>, n: number): $Array<V>
Ar.dropLast([1, 2, 3], 2) // [1]

Source Tests

Ar.takeFirstWhile

Create an Array containing all the items of collection preceding the item for which predicateFn returns false.

takeFirstWhile<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): $Array<V>
Ar.takeFirstWhile([1, 3, 4, 7], Mth.isOdd) // [1]

Source Tests

Ar.dropFirstWhile

Create an Array containing all the items of collection following and including the first item for which predicateFn returns false.

dropFirstWhile<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): $Array<V>
Ar.dropFirstWhile([1, 3, 4, 7], Mth.isOdd) // [3, 4, 7]

Source Tests

Ar.takeLastWhile

Create an Array containing all the items of collection following the first item when iterating from the end for which predicateFn returns false.

takeLastWhile<V>(
  collection: Collection<V>,
  predicateFn: V => boolean,
): $Array<V>
Ar.takeLastWhile([1, 3, 4, 5, 7], Mth.isOdd) // [5, 7]

Source Tests

Ar.dropLastWhile

Create an Array containing all the items of collection preceding and including the first item when iterating from the end for which predicateFn returns false.

dropLastWhile<V>(
  collection: Collection<V>,
  predicateFn: V => boolean,
): $Array<V>
Ar.dropLastWhile([1, 3, 4, 7], Mth.isOdd) // [1, 3, 4]

Source Tests

Divide

Ar.chunk

Create an array of Arrays which are chunks of given collection of given size.

If the collection doesn't divide evenly, the final chunk will be smaller than the rest.

chunk<V>(
  collection: Collection<V>,
  size: number,
): $Array<$Array<V>>
Ar.chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]

Source Tests

Ar.partition

Create a tuple of Arrays containing items of collection which match and don't match predicateFn respectively.

More effecient than using multiple Ar.filter calls.

partition<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): [$Array<V>, $Array<V>]
Ar.partition([1, 2, 3, 4], x => Mth.isEven(x)) // [[2, 4], [1, 3]]

Source Tests

Ar.slice

Create an Array containing a subset of values in collection.

Note that this is not a way to clone an Array, if given an Array and indices corresponding to its full size it returns the original array.

Either index can be negative, in which case they are counted from the end of the collection, with last element being at index `-1`.

slice<V>(
  collection: Collection<V>,
  startIndexInclusive: number,
  endIndexExclusive?: number,
): $Array<V>
Ar.slice([1, 2, 3, 4, 5], 1, 2) // [2, 3]
Ar.slice([1, 2, 3, 4, 5], -3, -1) // [3, 4]

Source Tests

Ar.splice

Create an Array containing a subset of values in collection with any given items added, with deleteCount original values removed.

Note that unlikely Array.prototype.splice this function returns the new array, not the deleted items.

splice<V>(
  collection: Collection<V>,
  startIndex: number,
  deleteCount?: number,
  ...items: $Array<V>
): $Array<V>
Ar.splice([1, 2, 3, 4], 1, 2, 5, 6) // [1, 5, 6, 4]

Source Tests

Ar.splitAt

Create a tuple of arrays containing the first n items and all but the first n items of given collection.

splitAt<V>(
  collection: Collection<V>,
  n: number,
): [$Array<V>, $Array<V>]
Ar.splitAt([1, 2, 3], 2) // [[1, 2], [3]]

Source Tests

Ar.span

Create two arrays, the first containing all the items of collection preceding the first item for which predicateFn returns false, the second containing the rest.

span<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): [$Array<V>, $Array<V>]
Ar.span([1, 3, 4, 7], Mth.isOdd) // [[1], [3, 4, 7]]

Source Tests

Combine

Ar.prepend

Create a new array containing item followed values of collection.

prepend<V>(collection: Collection<V>, item: V): $Array<V>
Ar.prepend([2, 3], 1) // [1, 2, 3]

Source Tests

Ar.append

Create a new array containing values of collection followed by item.

append<V>(collection: Collection<V>, item: V): $Array<V>
Ar.append([1, 2], 3) // [1, 2, 3]

Source Tests

Ar.concat

Concatenate multiple arrays together.

concat<V>(...collections: $Array<Collection<V>>): $Array<V>
Ar.concat([1, 2], [3, 4]) // [1, 2, 3, 4]

Source Tests

Ar.flatten

Concatenate a collection of arrays together.

flatten<V>(
  collectionOfArrays: Collection<Collection<V>>,
): $Array<V>
Ar.flatten([[1, 2], [3, 4]]) // [1, 2, 3, 4]

Source Tests

Ar.zip

Join collections into an Array of tuples of values from each collection.

The resulting array has the same length as the smallest given collection. Excess values are ignored.

zip<Cs: $Array<Collection<mixed>>>(
  ...collections: Cs
): $Array<TupleOfValues<Cs>>
Ar.zip([1, 2], ['a', 'b'], [5, 6]) // [[1, 'a', 5], [2, 'b', 6]]

Source Tests

Ar.zipWith

Join multiple collections into an Array of values resulting from calling fn on items from each collection.

Note that this function has unusual order of arguments because JavaScript enforces that the rest parameter is the last one. The resulting array has the same length as the smallest given collection. Excess values are ignored.

zipWith<I, Cs: $Array<Collection<I>>, O>(
  fn: (...collections: TupleOfValues<Cs>) => O,
  ...collections: Cs
): $Array<O>
Ar.zipWith((a, b) => a * b, [1, 2, 3], [5, 6, 7]) // [5, 12, 21]

Source Tests

Ar.unzip

Join a collection of tuples into a tuple of Arrays.

unzip<T: $Array<mixed>>(
  collection: Collection<T>,
): $TupleMap<T, <V>(V) => $Array<V>>
Ar.unzip([[1, 'a', 5], [2, 'b', 6]]) // [[1, 2], ['a', 'b'], [5, 6]]

Source Tests

Ar.product

Join collections into an Array of tuples of values of all combinations from each collection.

The resulting array has the length which is the product of the lengths of each collection.

product<Cs: $Array<Collection<mixed>>>(
  ...collections: Cs
): $Array<TupleOfValues<Cs>>
Ar.product([1, 2], ['a', 'b']) // [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]

Source Tests

Transform

Ar.map

Create a new array by calling given fn on each value of collection.

map<KFrom, VFrom, VTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  fn: (VFrom, KFrom) => VTo,
): $Array<VTo>
Ar.map([1, 2], x => x * 2) // [2, 4]

Source Tests

Ar.mapAsync

Create a promise of an Array by calling given async fn on each value of collection.

Executes fn on all items in collection concurrently.

mapAsync<KFrom, VFrom, VTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  fn: (VFrom, KFrom) => Promise<VTo>,
): Promise<$Array<VTo>>
await Ar.mapAsync([1, 2], async x => x * 2) // [2, 4]

Source Tests

Ar.mapMaybe

Create a new array by calling given fn on each value of collection and and including the result if it is not null or undefined.

Equivalent to using map followed by filterNulls, for simplicity and improved performance.

mapMaybe<KFrom, VFrom, VTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  fn: (VFrom, KFrom) => ?VTo,
): $Array<VTo>
Ar.mapMaybe([1, 2, 3], x => Math.isOdd(x) ? x * x : null) // [1, 9]

Source Tests

Ar.mapFlat

Create a new array by calling given fn on each value of collection and flattening the results.

Equivalent to using map followed by flatten, for simplicity and improved performance.

mapFlat<KFrom, VFrom, VTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  fn: (VFrom, KFrom) => Collection<VTo>,
): $Array<VTo>
Ar.mapFlat([1, 2], x => [- 1, x + 1]) // [0, 2, 1, 3]

Source Tests

Ar.scan

Create an Array of values based on a reduction of given collection.

Similar to Cl.reduce but instead of returning the final value accumulates all the intermediate accumulators.

scan<I, O>(
  collection: Collection<I>,
  initialValue: O,
  fn: (O, I) => O,
): $Array<O>
Ar.scan([1, 2, 3, 4], 0, (acc, x) => acc + x) // [1, 3, 6, 10]

Source Tests

Order

Ar.reverse

Create an Array containing the items of collection in reverse order.

reverse<V>(collection: Collection<V>): $Array<V>
Ar.reverse([1, 2, 3]) // [3, 2, 1]

Source Tests

Ar.sort

Create an Array of values in collection sorted.

The result of calling compareFn on values a and b determines their order: negative number: a, b positive number: b, a zero: the order stays the same as it was in collection The default compareFn is `(a, b) => a > b ? 1 : a < b ? -1 : 0`, which sorts numbers and strings in ascending order (from small to large, from early in the alphabet to later in the alphabet).

This sort preserves the order of elements when compareFn returns 0 at the cost of using more memory.

sort<K, V>(
  collection: KeyedCollection<K, V>,
  compareFn?: (
    aItem: V,
    bItem: V,
    aKey: K,
    bKey: K,
  ) => number = defaultCompareFn,
): $Array<V>
Ar.sort([3, 2, 4, 1]) // [1, 2, 3, 4]
Ar.sort(['c', 'b', 'd', 'a']) // ['a', 'b', 'c', 'd']

Source Tests

Ar.sortBy

Create an Array of values in collection sorted by the scalar computed by calling scalarFn on each value.

The result of calling compareFn on scalars a and b determines the order of the corresponding values: negative number: a, b positive number: b, a zero: the order stays the same as it was in collection The default compareFn is `(a, b) => a > b ? 1 : a < b ? -1 : 0`, which sorts numbers and strings in ascending order (from small to large, from early in the alphabet to later in the alphabet).

This sort preserves the order of elements when compareFn returns 0 at the cost of using more memory.

sortBy<K, V, S>(
  collection: KeyedCollection<K, V>,
  scalarFn: (V, K) => S,
  compareFn?: (a: S, b: S) => number = defaultCompareFn,
): $Array<V>
Ar.sortBy([3, 2, 4, 1], n => n % 3) // [3, 4, 1, 2]

Source Tests

Ar.sortUnstable

Create an Array of values in collection sorted.

This sort doesn't preserve the order of elements when compareFn returns 0 and is more memory efficient than Ar.sort.

The result of calling compareFn on values a and b determines their order: negative number: a, b positive number: b, a zero: the order is undetermined The default compareFn is `(a, b) => a > b ? 1 : a < b ? -1 : 0`, which sorts numbers and strings in ascending order (from small to large, from early in the alphabet to later in the alphabet).

sortUnstable<V>(
  collection: Collection<V>,
  compareFn?: (V, V) => number = defaultCompareFn,
): $Array<V>
Ar.sortUnstable([3, 2, 4, 1]) // 1, 2, 3, 4

Source Tests

Cl (collection)

This module provides functions which operate on collections (Arrays, Maps, Sets) and return a scalar or key/value type.

import {Cl} from 'jfl'

Check

Cl.equals

Returns whether given collections are equal.

All items must be strictly equal.

equals<V, C: Collection<V>>(
  first: C,
  ...rest: $Array<C>
): boolean
Cl.equals([1, 2], [1, 2]) // true

Source Tests

Cl.equalsNested

Returns whether given collections and any nested collections are equal.

Any contained collections must deeply equal, all other items must be strictly equal.

equalsNested<C: mixed>(first: C, ...rest: $Array<C>): boolean
Cl.equalsNested([[1], [2], 3], [[1], [2], 3]) // true

Source Tests

Cl.isEmpty

Returns true when collection is empty.

isEmpty<V>(collection: Collection<V>): boolean
Cl.isEmpty($Ar()) // true
Cl.isEmpty($Mp()) // true
Cl.isEmpty($St()) // true

Source Tests

Cl.count

Get the size of given collection.

count<V>(collection: Collection<V>): number
Cl.count([1, 2, 3]) // 3
Cl.count($Mp({a: 1, b: 3})) // 2

Source Tests

Cl.contains

Returns whether given collection contains given value.

contains<V>(collection: Collection<V>, value: V): boolean
Cl.contains([2, 4, 3], 1) // true
Cl.contains($St(2, 4, 3), 4) // true
Cl.contains($Mp({a: 1, b: 3}), 1) // true

Source Tests

Cl.containsKey

Returns whether given key exists in keyed collection.

containsKey<K, V>(
  collection: KeyedCollection<K, V>,
  key: K,
): boolean
Cl.containsKey([2, 4, 3], 1) // true
Cl.containsKey($St(2, 4, 3), 4) // true
Cl.containsKey($Mp({a: 1, b: 3}), 'a') // true

Source Tests

Cl.any

Returns whether some values in collection satisfy predicateFn.

When no predicateFn is given returns true when collection contains at least one true value.

function any(collection: Collection<boolean>): boolean
function any<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): boolean
Cl.any([1, 5, 4], n => Mth.isEven(n)) // true
Cl.any([true, false]) // true

Source Tests

Cl.every

Returns whether all values in collection satisfy predicateFn.

When no predicateFn is given returns true when collection contains only true values.

function every(collection: Collection<boolean>): boolean
function every<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): boolean
Cl.every([], n => Mth.isOdd(n)) // true
Cl.every([1, 5, 3], n => Mth.isOdd(n)) // true
Cl.every([true, true]) // true

Source Tests

Cl.isSorted

Returns whether given collection is sorted.

The result of calling compareFn on values a and b determines their order: negative number: a, b positive number: b, a zero: the order stays the same as it was in collection The default compareFn is `(a, b) => a > b ? 1 : a < b ? -1 : 0`, which sorts numbers and strings in ascending order (from small to large, from early in the alphabet to later in the alphabet).

isSorted<V>(
  collection: Collection<V>,
  compareFn?: (a: V, b: V) => number = defaultCompareFn,
): boolean
Cl.isSorted([2, 4, 3]) // true
Cl.isSorted($St('b', 'c', 'd')) // true

Source Tests

Cl.isSortedBy

Returns whether given collection is sorted by the scalar computed by calling scalarFn on each value.

The result of calling compareFn on values a and b determines their order: negative number: a, b positive number: b, a zero: the order stays the same as it was in collection The default compareFn is `(a, b) => a > b ? 1 : a < b ? -1 : 0`, which sorts numbers and strings in ascending order (from small to large, from early in the alphabet to later in the alphabet).

isSortedBy<V, S>(
  collection: Collection<V>,
  scalarFn: V => S,
  compareFn?: (a: S, b: S) => number = defaultCompareFn,
): boolean
Cl.isSortedBy([3, 4, 1, 2], n => n % 3) // true

Source Tests

Select

Cl.find

Returns the first value for which predicateFn returns true in collection, if any.

find<V>(
  collection: Collection<V>,
  predicateFn: V => boolean,
): ?V
Cl.find([2, 4], n => Mth.isOdd(n)) // null
Cl.find([2, 4, 3], n => Mth.isOdd(n)) // 3

Source Tests

Cl.findX

Returns first value for which predicateFn returns true in collection.

findX<V>(
  collection: Collection<V>,
  predicateFn: V => boolean,
): V
Cl.findX([2, 4, 3], n => Mth.isOdd(n)) // 3

Source Tests

Cl.findKey

Returns the key of the first value for which predicateFn returns true in collection, if any.

findKey<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): ?K
Cl.findKey([2, 4, 3], n => Mth.isOdd(n)) // 2

Source Tests

Cl.findKeyX

Returns the key of the first value for which predicateFn returns true in collection.

findKeyX<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): K
Cl.findKeyX([2, 4, 3], n => Mth.isOdd(n)) // 2

Source Tests

Cl.first

Returns the first value in collection if it's not empty, null otherwise.

first<V>(collection: Collection<V>): ?V
Cl.first($Ar()) // null
Cl.first([1, 3]) // 1

Source Tests

Cl.firstX

Returns the first value in collection if it's not empty, throws otherwise.

firstX<V>(collection: Collection<V>): V
Cl.firstX([1, 3]) // 1

Source Tests

Cl.onlyX

Returns the one and only value in collection, throws otherwise.

onlyX<V>(collection: Collection<V>): V
Cl.onlyX([1]) // 1
Cl.onlyX([]) // throws
Cl.onlyX([1, 2]) // throws

Source Tests

Cl.last

Returns the last value in collection if it's not empty, null otherwise.

last<V>(collection: Collection<V>): ?V
Cl.last($Ar()) // null
Cl.last([1, 3]) // 3

Source Tests

Cl.lastX

Returns the last value in collection if it's not empty, throws otherwise.

lastX<V>(collection: Collection<V>): V
Cl.lastX([1, 3]) // 3

Source Tests

Cl.at

Returns the value at given iteration index or null.

For accessing values using corresponding keys use Map.prototype.get or Set.prototype.has, which are all correctly typed.

at<V>(collection: Collection<V>, index: number): ?V
Cl.at($St(), 2) // null
Cl.at($St('a', 'b', 'c'), 2) // 'c'

Source Tests

Cl.atX

Returns the value at given iteration index or throws.

atX<V>(collection: Collection<V>, index: number): V
Cl.atX($St('a', 'b', 'c'), 2) // 'c'

Source Tests

Cl.atFromEnd

Returns the value at index as if iterating through the collection in reverse order or null.

atFromEnd<V>(collection: Collection<V>, index: number): ?V
Cl.atFromEnd([], 2) // null
Cl.atFromEnd([1, 2, 3, 4], 0) // 4
Cl.atFromEnd([1, 2, 3, 4], 3) // 1

Source Tests

Cl.atFromEndX

Returns the value at index as if iterating through the collection in reverse order or throws.

atFromEndX<V>(collection: Collection<V>, index: number): V
Cl.atFromEndX([1, 2, 3, 4], 0) // 4
Cl.atFromEndX([1, 2, 3, 4], 3) // 1

Source Tests

Cl.atDynamic

Return element at index counting from start if it's positive and counting from end if it's negative, with last element being at index `-1`.

atDynamic<V>(collection: Collection<V>, index: number): ?V
Cl.atDynamic([1, 2, 3, 4], 0) // 1
Cl.atDynamic([1, 2, 3, 4], -2) // 3

Source Tests

Cl.firstKey

Returns the first key in collection if it's not empty, null otherwise.

firstKey<K, V>(collection: KeyedCollection<K, V>): ?K
Cl.firstKey($Mp()) // null
Cl.firstKey($Mp({a: 1, b: 2})) // 'a'

Source Tests

Cl.firstKeyX

Returns the first key in collection if it's not empty, throws otherwise.

firstKeyX<K, V>(collection: KeyedCollection<K, V>): K
Cl.firstKeyX($Mp({a: 1, b: 2})) // 1

Source Tests

Cl.lastKey

Returns the last key in collection if it's not empty, null otherwise.

lastKey<K, V>(collection: KeyedCollection<K, V>): ?K
Cl.lastKey($Mp()) // null
Cl.lastKey($Mp({a: 1, b: 2})) // 'b'

Source Tests

Cl.lastKeyX

Returns the last key in collection if it's not empty, throws otherwise.

lastKeyX<K, V>(collection: KeyedCollection<K, V>): K
Cl.lastKeyX($Mp({a: 1, b: 2})) // 'b'

Source Tests

Transform

Cl.forEach

Execute fn for every value and key in collection.

forEach<K, V>(
  collection: KeyedCollection<K, V>,
  fn: (V, K, KeyedCollection<K, V>) => void,
): void
Cl.forEach([1, 2, 3], (n, index, array) => {}) // void

Source Tests

Cl.reduce

Reduce the collection to a single value using fn.

If no initialValue is passed in, the collection must be non-empty.

function reduce<K, V, A>(
  collection: KeyedCollection<K, V>,
  fn: (V, V, K, KeyedCollection<K, V>) => V,
): V
function reduce<K, V, A>(
  collection: KeyedCollection<K, V>,
  fn: (A, V, K, KeyedCollection<K, V>) => A,
  initialValue: A,
): A
Cl.reduce([2, 4, 3], (acc, x) => acc + x) // 9

Source Tests

Mp (map)

This module provides functions which operate on collections (Arrays, Maps, Sets) and return read-only (immutable) Maps.

import {Mp, $Mp} from 'jfl'

Construct

$Mp

Create a Map.

If your keys aren't strings, prefer Mp.of.

$Mp<K: string, V>(object?: {[key: K]: V}): $Map<K, V>
$Mp({a: 1, b: 2, c: 3}) // Map {a => 1, b => 2, c => 3}

Source Tests

Mp.of

Create a Map from given pairs of keys and values.

of<K, V>(...pairs: $Array<[K, V]>): $Map<K, V>
Mp.of([0, 2], [4, 2]) // Map {0 => 2, 4 => 2}

Source Tests

Mp.from

Convert any keyed collection to a Map.

Note that this is not a way to clone a Map, if passed a Map, the same map will be returned.

from<K, V>(collection: KeyedCollection<K, V>): $Map<K, V>
Mp.from([1, 2, 3]) // Mp.of([0, 1], [1, 2], [2, 3])
Mp.from(Set('a', 'b', 'c')) // $Mp({a: 'a', b: 'b', c: 'c'})

Source Tests

Mp.fromAsync

Convert any keyed collection of promises to a Map.

async function fromAsync<K, V>(
  collection: KeyedCollection<K, Promise<V>>,
): Promise<$Map<K, V>>
Mp.fromAsync($Mp({a: (async () => 1)()}) // $Mp({a: 1})

Source Tests

Mp.fromValues

Create a Map where each value comes from collection and its key is the result of calling getKey on it.

fromValues<KFrom, KTo, VTo>(
  collection: KeyedCollection<KFrom, VTo>,
  getKey: (VTo, KFrom) => KTo,
): $Map<KTo, VTo>
Mp.fromValues([2, 3], n => n ** 2) // Mp.of([4, 2], [9, 3])

Source Tests

Mp.fromKeys

Create a Map where each key comes from collection and its value is the result of calling getValue on it.

fromKeys<KFrom, KTo, VTo>(
  collection: KeyedCollection<KFrom, KTo>,
  getValue: (KTo, KFrom) => VTo,
): $Map<KTo, VTo>
Mp.fromKeys([2, 3], n => n ** 2) // Mp.of([2, 4], [3, 9])

Source Tests

Mp.fromKeysAsync

Create a promise of a Map where each key comes from collection and its value is the result of calling getValue on it.

async function fromKeysAsync<KFrom, KTo, VTo>(
  collection: KeyedCollection<KFrom, KTo>,
  getValue: (KTo, KFrom) => Promise<VTo>,
): Promise<$Map<KTo, VTo>>
await Mp.fromKeysAsync([2], async n => n ** 2) // Mp.of([2, 4])

Source Tests

Mp.fromEntries

Create a Map from a collection of entries, i.e. (key, value) pairs.

fromEntries<K, V>(collection: Collection<[K, V]>): $Map<K, V>
Mp.fromEntries($St([2, 3])) // Mp.of([2, 3])

Source Tests

Mp.zip

Create a Map from given keys and values.

If there are more keys than values or vice versa, ignores the excess items.

zip<K, V>(
  keys: Collection<K>,
  values: Collection<V>,
): $Map<K, V>
Mp.zip(['a', 'b'], [3, 4]) // $Mp({a: 3, b: 4})

Source Tests

Mp.toObject

Create an Object from a string-keyed Map.

toObject<K: string, V>(
  collection: KeyedCollection<K, V>,
): {[key: K]: V}
Mp.toObject($Mp({a: 1, b: 2})) // {a: 1, b: 2}

Source Tests

Mp.mutable

Convert any keyed collection to a mutable Map.

If a Map is given, it will be cloned.

mutable<K, V>(collection: KeyedCollection<K, V>): Map<K, V>
Mp.mutable($Mp({a: 1, b: 2})) // Map {a => 1, b => 2}

Source Tests

Check

Mp.isMap

Returns whether given value is a Map.

isMap(argument: mixed): bool
Mp.isMap([1, 2, 3]) // false

Source Tests

Mp.equals

Returns whether given Maps are equal.

All values and keys must be strictly equal.

equals<K, V>(
  map: $Map<K, V>,
  ...maps: $Array<$Map<K, V>>
): boolean
Mp.equals($Mp({a: 1, b: 2}), $Mp({a: 1, b: 2})) // true

Source Tests

Mp.equalsOrderIgnored

Returns whether given Maps contain the same key/value pairs.

All values and keys must be strictly equal.

equalsOrderIgnored<K, V>(
  map: $Map<K, V>,
  ...maps: $Array<$Map<K, V>>
): boolean
Mp.equalsOrderIgnored($Mp({a: 1, b: 2}), $Mp({b: 2, a: 1})) // true

Source Tests

Mp.equalsNested

Returns whether given Maps are equal.

Any collection values or keys must deeply equal, all other values and keys must be strictly equal.

equalsNested<K, V>(
  map: $Map<K, V>,
  ...maps: $Array<$Map<K, V>>
): boolean
Mp.equalsNested(Mp.of([[0], [1]]]), Mp.of([[0], [1]]])) // true

Source Tests

Combine

Mp.set

Create a new Map by adding or replacing value under key in given keyed collection.

set<K, V>(
  collection: KeyedCollection<K, V>,
  key: K,
  value: V,
): $Map<K, V>
Mp.set($Mp({a: 1}), 'b', 2) // $Mp({a: 1, b: 2})

Source Tests

Mp.merge

Create a new Map by merging all given collections. Later values will override earlier values.

merge<K, V>(
  ...collections: $Array<KeyedCollection<K, V>>
): $Map<K, V>
Mp.merge($Mp({a: 1, b: 2}), $Mp({a: 2, c: 3})) // $Mp({a: 2, b: 2, c: 3})

Source Tests

Select

Mp.getX

Returns the value in map for given key or throws.

If you don't know whether the map contains the key use Map.prototype.get.

getX<K, V>(map: $Map<K, V>, key: K): V
Mp.getX($Mp({a: 2}), 'a') // 2

Source Tests

Mp.diffByKey

Create a new Map which has all the keys and corresponding values from collection except for the keys that appear in any of the given collections.

diffByKey<K, V>(
  collection: KeyedCollection<K, V>,
  ...collections: $Array<KeyedCollection<K, V>>
): $Map<K, V>
Mp.diffByKey($Mp({a: 1, b: 2}), $Mp({b: 3}), $Mp({c: 4})) // $Mp({a: 1})

Source Tests

Mp.filter

Create a new Map by filtering out keys and values for which predicateFn returns false.

filter<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): $Map<K, V>
Mp.filter($Mp({a: 1, b: 2}), x => Mth.isOdd(x)) // $Mp({a: 1})
Mp.filter(Mp.of([0, 1], [3, 4]), (_, x) => Mth.isOdd(x)) // Mp.of([3, 4])

Source Tests

Mp.filterAsync

Create a promise of a Map by filtering out values in collection for which async predicateFn returns false.

Executes predicateFn on all key value pairs in collection concurrently.

async function filterAsync<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => Promise<boolean>,
): Promise<$Map<K, V>>
Mp.filterAsync([1, 2, 3], async x => Mth.isOdd(x)) // $Mp({0: 1, 2: 3})

Source Tests

Mp.filterNulls

Create a new Map by filtering out null and undefined values.

Here because its type is more specific then the generic filter function.

filterNulls<K, V>(
  collection: KeyedCollection<K, ?V>,
): $Map<K, V>
Mp.filterNulls([1, null, 3]) // Mp.of([0, 1], [2, 3])

Source Tests

Mp.selectKeys

Create a new Map which has all the keys and corresponding values from map for the keys that appear in keys.

The order of the keys in the resulting map is the order in which they appear first in keys.

selectKeys<K, V>(
  collection: KeyedCollection<K, V>,
  keys: Collection<K>,
): $Map<K, V>
Mp.selectKeys($Mp({a: 1, b: 2, c: 3}), ['c', 'b']) // $Mp({c: 3, b: 2})

Source Tests

Mp.unique

Create a Map of values from collection with each value included only once.

Later keys for the same value overwrite previous ones.

unique<K, V>(collection: KeyedCollection<K, V>): $Map<K, V>
Mp.unique($Mp{a: 1, b: 2, c: 1}) // $Mp({c: 1, b: 2})

Source Tests

Mp.uniqueBy

Create a Map of values from collection with each value included only once, where value equivalence is determined by calling identityFn on each value. Later values and keys overwrite previous ones.

uniqueBy<K, V>(
  collection: KeyedCollection<K, V>,
  identityFn: (V, K) => mixed,
): $Map<K, V>
Mp.uniqueBy($Mp({a: 1, b: 2, c: 3}), Mth.isOdd) // $Mp({c: 3, b: 2})

Source Tests

Mp.takeFirst

Create a Map containing the first n key value pairs of collection.

takeFirst<K, V>(
  collection: KeyedCollection<K, V>,
  n: number,
): $Map<K, V>
Mp.takeFirst($Mp({a: 1, b: 2, c: 3}), 2) // $Mp({a: 1, b: 2})

Source Tests

Mp.dropFirst

Create a Map containing all but the first n key value pairs of collection.

dropFirst<K, V>(
  collection: KeyedCollection<K, V>,
  n: number,
): $Map<K, V>
Mp.dropFirst($Mp({a: 1, b: 2, c: 3}), 2) // $Mp({c: 3})

Source Tests

Transform

Mp.map

Create a new Map by calling given fn on each value and key of collection.

map<KFrom, VFrom, VTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  fn: (VFrom, KFrom) => VTo,
): $Map<KFrom, VTo>
Mp.map($Mp({a: 1, b: 2}}), (x) => x * 2) // $Mp({a: 2, b: 4})

Source Tests

Mp.mapAsync

Create a promise of a Map by calling given async fn on each value and key of collection.

Executes fn on all items in collection concurrently.

async function mapAsync<KFrom, VFrom, VTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  fn: (VFrom, KFrom) => Promise<VTo>,
): Promise<$Map<KFrom, VTo>>
await Mp.mapAsync($Mp({a: 1}}), async x => x * 2) // $Mp({a: 2})

Source Tests

Mp.mapMaybe

Create a new Map by calling given fn on each value and key of collection and and including the result if it is not null or undefined.

Equivalent to using map followed by filterNulls, for simplicity and improved performance.

mapMaybe<KFrom, VFrom, VTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  fn: (VFrom, KFrom) => ?VTo,
): $Map<KFrom, VTo>
Mp.mapMaybe($Mp({a: 2}), (x) => Mth.isOdd(x) ? x : null) // $Mp()

Source Tests

Mp.mapFlat

Create a new Map by calling given fn on each value and key of collection and flattening the results.

Equivalent to using map followed by flatten, for simplicity and improved performance.

mapFlat<KFrom, KTo, VFrom, VTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  fn: (VFrom, KFrom) => KeyedCollection<KTo, VTo>,
): $Map<KTo, VTo>
Mp.mapFlat([1, 3], n => Mp.of([+ 1, 'a'])) // Mp.of([2, 'a'], [4, 'a'])

Source Tests

Mp.mapToEntries

Create a new Map by calling given fn on each value and key of collection.

fn must return new entries to populate the map.

mapToEntries<KFrom, VFrom, KTo, VTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  fn: (VFrom, KFrom) => [KTo, VTo],
): $Map<KTo, VTo>
Mp.mapToEntries(['a', 'b'], (x, i) => [x, i]) // $Mp({a: 0, b: 1})

Source Tests

Mp.pull

Create a new Map using keys provided by keyFn and values provided by valueFn applied to each key/value of collection.

When keyFn returns the same key for multiple values only the last value will be present in the new Map. Values for which keyFn returns null or undefined are ommited.

pull<KFrom, VFrom, KTo, VTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  keyFn: (VFrom, KFrom) => ?KTo,
  valueFn: (VFrom, KFrom) => VTo,
): $Map<KTo, VTo>
Mp.pull([2, 3], n => n - 2, n => n ** 2) // Mp.of([0, 4], [1, 9])

Source Tests

Mp.group

Create a new Map using keys provided by keyFn and values provided by valueFn applied to each key/value of collection. Values with identitical keys are grouped into Arrays.

Values for which keyFn returns null or undefined are ommited.

function group<K, V>(
  collection: KeyedCollection<K, V>,
): $Map<V, $Array<V>>
function group<KFrom, VFrom, KTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  keyFn: (VFrom, KFrom) => ?KTo,
): $Map<KTo, $Array<VFrom>>
function group<KFrom, VFrom, KTo, VTo>(
  collection: KeyedCollection<KFrom, VFrom>,
  keyFn: (VFrom, KFrom) => ?KTo,
  valueFn: (VFrom, KFrom) => VTo,
): $Map<KTo, $Array<VTo>>
Mp.group([1, 1, 3]) // Mp.of([1, [1, 1]], [3, [3]])
Mp.group([1, 2, 3], n => n % 2) // Mp.of([1, [1, 3]], [0, [2]])

Source Tests

Mp.flip

Create a new Map with keys and values being the values and keys of collection respectively.

flip<K, V>(collection: KeyedCollection<K, V>): $Map<V, K>
Mp.flip($Mp({a: 'A', b: 'B'})) // $Mp({A: 'a', B: 'b'})

Source Tests

Mp.countValues

Create a new Map with keys being the values of collection and values being the number of times each value occured in collection.

undefined and null are valid values of collection.

countValues<V>(collection: Collection<V>): $Map<V, number>
Mp.countValues($Mp({a: 'x', b: 'x', c: 'y'})) // $Mp({x: 2, y: 1})

Source Tests

Mp.flatten

Create a new Map by concatenating all keyed collections in given collection.

flatten<K, V>(
  collection: Collection<KeyedCollection<K, V>>,
): $Map<K, V>
Mp.flatten([$Mp({a: 1}), $Mp({b: 2, c: 3})]) // $Mp({a: 1, b: 2, c: 3})

Source Tests

Divide

Mp.chunk

Create an array of Maps which are chunks of given collection of given size.

If the collection doesn't divide evenly, the final chunk will be smaller than the rest.

chunk<K, V>(
  collection: KeyedCollection<K, V>,
  size: number,
): $Array<$Map<K, V>>
Mp.chunk($Mp({a: 1, b: 2, c: 3}), 2) // [$Mp({a: 1, b: 2}), $Mp({c: 3})]

Source Tests

Mp.partition

Create a tuple of Maps containing keys and items of collection which match and don't match predicateFn respectively.

More effecient than using multiple Mp.filter calls.

partition<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): [$Map<K, V>, $Map<K, V>]
Mp.partition($Mp({a: 2}), x => Mth.isOdd(x)) // [$Mp(), $Mp({a: 2})]

Source Tests

Ordering

Mp.reverse

Create a Map containing the keys and items of collection in reverse order.

reverse<K, V>(collection: KeyedCollection<K, V>): $Map<K, V>
Mp.reverse($Mp({a: 1, b: 2, c: 3})) // $Mp({c: 3, b: 2, a: 1})

Source Tests

Mp.sort

Create a Map of keys and values in collection sorted by value.

The result of calling compareFn on values a and b determines their order: negative number: a, b positive number: b, a zero: the order stays the same as it was in collection The default compareFn is `(a, b) => a > b ? 1 : a < b ? -1 : 0`, which sorts numbers and strings in ascending order (from small to large, from early in the alphabet to later in the alphabet).

sort<K, V>(
  collection: KeyedCollection<K, V>,
  compareFn?: (
    aItem: V,
    bItem: V,
    aKey: K,
    bKey: K,
  ) => number = defaultCompareFn,
): $Map<K, V>
Mp.sort($Mp({a: 3, b: 1, c: 2})) // $Mp({b: 1, c: 2, a: 3})
Mp.sort($Mp({x: 'c', y: 'b', z: 'a'})) // $Mp({z: 'a', y: 'b', x: 'c'})

Source Tests

Mp.sortBy

Create a Map of keys and values in collection sorted by the scalar computed by calling scalarFn on each value and key.

The result of calling compareFn on scalars a and b determines the order of the corresponding values: negative number: a, b positive number: b, a zero: the order stays the same as it was in collection The default compareFn is `(a, b) => a > b ? 1 : a < b ? -1 : 0`, which sorts numbers and strings in ascending order (from small to large, from early in the alphabet to later in the alphabet).

sortBy<K, V, S>(
  collection: KeyedCollection<K, V>,
  scalarFn: (V, K) => S,
  compareFn?: (a: S, b: S) => number = defaultCompareFn,
): $Map<K, V>
Mp.sortBy($Mp({a: 3, b: 4, c: 2}), n => n % 3) // $Mp({b: 1, c: 2, a: 3})

Source Tests

Mth (math)

This module provides functions which complement methods of Math and Number built-ins or operate on collections (Arrays, Maps, Sets) and return numbers.

import {Cl} from 'jfl'

Check

Mth.isOdd

Returns true when n is an odd integer.

isOdd(n: number): boolean
Mth.isOdd(-3) // true

Source Tests

Mth.isEven

Returns true when n is an even integer.

isEven(n: number): boolean
Mth.isEven(-10) // true

Source Tests

Mth.isNumber

Returns whether given value is a number.

Use these methods to check that a number `Number.isFinite(x)` is not a NaN or Infinity `Number.isInteger(x)` is an integer (including a possibly rounded large one) `Number.isSafeInteger(x)` is an integer (in representable range) `Number.isNaN(x)` is a NaN (such as the result of `Math.sqrt(-1)`)

isNumber(n: mixed): bool
Mth.isNumber("a") // false

Source Tests

Operators

Mth.pmod

Returns the remainder of dividing numerator by divisor, unlike the `%` operator the result will always be positive.

pmod(numerator: number, divisor: number): number
Mth.pmod(-13, 5) // 2

Source Tests

Mth.idiv

Returns the integer division of numerator by divisor.

idiv(numerator: number, divisor: number): number
Mth.idiv(10, 3) // 3

Source Tests

Mth.divx

Returns the result of dividing numerator by divisor. Throws an error if divisor is 0.

divx(numerator: number, divisor: number): number
Mth.divx(5, 0) // throws an error

Source Tests

Mth.idivx

Returns the integer division of numerator by divisor. Throws an error if divisor is 0.

idivx(numerator: number, divisor: number): number
Mth.idivx(10, 0) // throws an error

Source Tests

Mth.clamp

Returns value if at least min, otherwise min, and at most max, otherwise max.

clamp(value: number, min: number, max: number): number
Mth.clamp(42, 0, 100) // 42
Mth.clamp(-42, 0, 100) // 0
Mth.clamp(142, 0, 100) // 100

Source Tests

Collections

Mth.min

Returns the smallest of all values in collection, null if it's empty.

min(collection: Collection<number>): ?number
Mth.min($Mp({a: 5, b: 2, c: 8})) // 2
Mth.min([]) // null

Source Tests

Mth.minBy

Returns the last item in collection for which valueFn returns the smallest number, null if collection is empty.

minBy<K, V>(
  collection: KeyedCollection<K, V>,
  valueFn: (V, K) => number,
): ?V
Mth.minBy([1, 2], n => -n) // 2
Mth.minBy([], n => -n) // null

Source Tests

Mth.max

Returns the largest of all values in collection, null if it's empty.

max(collection: Collection<number>): ?number
Mth.max($Mp({a: 5, b: 2, c: 8})) // 8
Mth.max([]) // null

Source Tests

Mth.maxBy

Returns the last item in collection for which valueFn returns the largest number, null if collection is empty.

maxBy<K, V>(
  collection: KeyedCollection<K, V>,
  valueFn: (V, K) => number,
): ?V
Mth.maxBy([1, 2], n => -n) // 1
Mth.maxBy([], n => -n) // null

Source Tests

Mth.mean

Returns the mean (average) of all values in collection, null if it's empty.

mean(collection: Collection<number>): ?number
Mth.mean($Mp({a: 1, b: 36, c: 2})) // 13
Mth.mean([]) // null

Source Tests

Mth.median

Returns the median (middle) of all values in collection, null if it's empty.

It the collection has an even number of items, the result will be the average of the two middle values.

median(collection: Collection<number>): ?number
Mth.median($Mp({a: 1, b: 36, c: 2})) // 2
Mth.median($Mp({a: 1, b: 36, c: 2, d: 3})) // 2.5
Mth.median([]) // null

Source Tests

Mth.sum

Returns the sum of all values in collection.

sum(collection: Collection<number>): number
Mth.sum([1, 2, 3]) // 6

Source Tests

Mth.product

Returns the product of all values in collection, 1 if it's empty.

product(collection: Collection<number>): number
Mth.product([2, 3, 4]) // 24
Mth.product([]) // 1

Source Tests

Bases

Mth.fromBase

Returns an integer parsed from a valid string representation in given base, throws otherwise.

base must be an integer between 2 and 36, throws otherwise.

For bases greater than 10, characters A-Z can indicate numerals greater than 9. A can stand for 10, B for 11 and so on up to Z for numeral 35.

fromBase(string: string, base: number): number
Mth.fromBase('01000', 2) // 8
Mth.fromBase('a01000', 2) // throws
Mth.fromBase('01000a', 2) // throws
Mth.fromBase('01000', 1) // throws

Source Tests

Mth.baseConvert

Returns a string representation in resultBase of a number parsed from a string representation in given inputBase.

inputBase and resultBase must be integers between 2 and 36, throws otherwise.

For bases greater than 10, characters A-Z can indicate numerals greater than 9. A can stand for 10, B for 11 and so on up to Z for numeral 35.

baseConvert(
  string: string,
  inputBase: number,
  resultBase: number,
): string
Mth.baseConvert('01000', 2, 4) // '20'
Mth.baseConvert('01000', 1, 4) // throws
Mth.baseConvert('01000', 2, 38) // throws

Source Tests

Mth.toBase

Returns a string representation of number in given base.

base must be an integer between 2 and 36, throws otherwise.

For bases greater than 10, characters A-Z indicate numerals greater than 9. A stands for 10, B for 11 and so on up to Z for numeral 35.

toBase(number: number, base: number): string
Mth.toBase(42, 3) // '1120'

Source Tests

REx (regexp)

This module provides functions for constructing RegExps.

Every RegExp consists of a string source and a string flags, which determine its behavior.

For using RegExps for matching strings use the Str module. For basic construction use JavaScript built-ins.

import {REx} from 'jfl'
const pattern = /hello \w+/i
const pattern = new RegExp("hello \\w+", "i")

Check

REx.isRegExp

Returns whether given value is a RegExp.

isRegExp(value: mixed): bool
REx.isRegExp(/a/) // true

Source Tests

REx.equals

Returns whether given RegExps are equal.

For patterns to be equal their source and flags must be strictly equal.

equals(pattern: RegExp, ...patterns: $Array<RegExp>): boolean
REx.equals(/a/i, /a/i) // true

Source Tests

Combine

REx.concat

Create a new RegExp by combining the sources of given patterns.

The new RegExp inherits the flags from the last given pattern.

concat(...patterns: $Array<RegExp>): RegExp
REx.concat(/a/, /b/m) // /ab/m

Source Tests

REx.append

Create a new RegExp by adding appended to the end of the source of the given pattern.

append(pattern: RegExp, appended: string | RegExp): RegExp
REx.append(/a/i, 'b') // /ab/i
REx.append(/a/i, /b/m) // /ab/i

Source Tests

REx.prepend

Create a new RegExp by adding prepended at the start of the source of the given pattern.

prepend(pattern: RegExp, prepended: string | RegExp): RegExp
REx.prepend(/a/i, 'b') // /ba/i
REx.prepend(/a/i, /b/m) // /ba/i

Source Tests

REx.addFlags

Returns a RegExp which includes flags from pattern and given flags.

This function throws if the given flags include invalid characters.

addFlags(pattern: RegExp, flags: string): RegExp
REx.addFlags(/a/g, 'i') // /a/gi

Source Tests

REx.removeFlags

Returns a RegExp which includes flags from pattern not present in given flags.

removeFlags(pattern: RegExp, flags: string): RegExp
REx.removeFlags(/a/g, 'gi') // /a

Source Tests

St (set)

This module provides functions which operate on collections (Arrays, Maps, Sets) and return read-only (immutable) Sets.

import {St, $St} from 'jfl'

Construction

$St

Create a Set.

Can contain any JS value. Maintains uniqueness.

$St<V>(...args: $Array<V>): $Set<V>
$St(1, 2, 3) // Set {1, 2, 3}

Source Tests

St.from

Convert any collection of values to a Set of values.

Note that this is not a way to clone a Set, if passed a Set, the same set will be returned.

from<V>(collection: Collection<V>): $Set<V>
St.from([1, 2, 3]) // $St(1, 2, 3)
St.from($Mp({a: 1, b: 2, c: 3})) // $St(1, 2, 3)

Source Tests

St.keys

Convert any collection with keys to a Set of keys.

Notably the keys of a Set are just its values. The keys of an Array are its indices.

keys<K>(collection: KeyedCollection<K, any>): $Set<K>
St.keys([5, 6]) // $St(0, 1)
St.keys($Mp({a: 2, b: 3})) // $St('a', 'b')
St.keys($St(3, 4) // $St(3, 4)

Source Tests

St.fromAsync

Convert any collection of awaitable promises of values to a single promise of a Set of values.

async function fromAsync<V>(
  collection: Collection<Promise<V>>,
): Promise<$Set<V>>
St.fromAsync([(async () => 1)(), (async () => 2)()]) // $St(1, 2)

Source Tests

St.mutable

Convert any collection of values to a mutable Set of values.

If a Set is given it will be cloned.

mutable<V>(collection: Collection<V>): Set<V>
St.mutable($St(1, 2, 3)) // Set {1, 2, 3}

Source Tests

Checks

St.isSet

Returns whether given argument is a Set.

isSet(argument: mixed): bool
St.isSet($St(1, 2, 3)) // true

Source Tests

St.equals

Returns whether given Sets are equal.

All items must be strictly equal.

equals<V>(set: $Set<V>, ...sets: $Array<$Set<V>>): boolean
St.equals([1, 2], [1, 2]) // true

Source Tests

St.equalsOrderIgnored

Returns whether given Sets contain the same values.

All items must be strictly equal.

equalsOrderIgnored<V>(
  set: $Set<V>,
  ...sets: $Array<$Set<V>>
): boolean
St.equalsOrderIgnored([1, 2], [2, 1]) // true

Source Tests

St.equalsNested

Returns whether given Sets and any nested collections are equal.

Any contained collections must deeply equal, all other items must be strictly equal.

equalsNested<V>(
  set: $Set<V>,
  ...sets: $Array<$Set<V>>
): boolean
Ar.equalsNested([[1], [2], 3], [[1], [2], 3]) // true

Source Tests

Combine

St.add

Create a Set with value added.

add<V>(collection: Collection<V>, value: V): $Set<V>
St.add($St(1, 2, 3), 4) // $St(1, 2, 3, 4)

Source Tests

St.union

Create a Set which is a union of all values in given collections.

union<V>(...collections: $Array<Collection<V>>): $Set<V>
St.union($St(1, 2, 3), $St(1, 4, 5)) // $St(1, 2, 3, 4, 5)

Source Tests

St.intersect

Create a Set which is an intersection of all values in given collections.

intersect<V>(...collections: $Array<Collection<V>>): $Set<V>
St.intersect($St(1, 2, 3), $St(2, 3, 6), $St(0, 1, 2)) // $St(2)

Source Tests

St.diff

Create a Set which has the values from collection that do not appear in any of the given collections.

diff<V>(
  collection: Collection<V>,
  ...collections: $Array<Collection<V>>
): $Set<V>
St.diff($St(1, 2, 3), $St(2, 4), $St(1, 2, 4)) // $St(3)

Source Tests

St.flatten

Create a Set which is a union of all values in given collections.

flatten<V>(collections: $Array<Collection<V>>): $Set<V>
St.flatten([$St(1, 2, 3), $St(1, 4, 5)]) // $St(1, 2, 3, 4, 5)

Source Tests

Select

St.filter

Create a new Set by filtering out values for which fn returns false.

filter<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): $Set<V>
St.filter($St(1, 2, 3), x => Mth.isOdd(x)) // $St(1, 3)

Source Tests

St.filterAsync

Create a promise of a Set by filtering out values in collection for which async fn returns false.

Executes predicateFn on all items in collection concurrently.

async function filterAsync<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => Promise<boolean>,
): Promise<$Set<V>>
St.filterAsync([1, 2, 3], async x => Mth.isOdd(x)) // $St(1, 3)

Source Tests

St.filterNulls

Create a new set by filtering out nulls and undefineds.

Here because its type is more specific then the generic filter function.

filterNulls<V>(collection: Collection<?V>): $Set<V>
St.filterNulls([1, null, 3]) // $St(1, 3)

Source Tests

St.filterKeys

Create a Set of keys corresponding to values passing given predicateFn.

filterKeys<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): $Set<K>
St.filterKeys($Mp({a: 1, b: 2}), n => Mth.isOdd(n)) // $St('a')

Source Tests

Transform

St.map

Create a new set by calling given fn on each value of collection.

map<K, VFrom, VTo>(
  collection: KeyedCollection<K, VFrom>,
  fn: (VFrom, K) => VTo,
): $Set<VTo>
St.map([1, 2], x => x * 2) // $St(2, 4)

Source Tests

St.mapAsync

Create a promise of a Set by calling given async fn on each value of collection.

Executes fn on all items in collection concurrently.

async function mapAsync<K, VFrom, VTo>(
  collection: KeyedCollection<K, VFrom>,
  fn: (VFrom, K) => Promise<VTo>,
): Promise<$Set<VTo>>
await St.mapAsync([1, 2], async x => x * 2) // $St(2, 4)

Source Tests

St.mapFlat

Create a new set by calling given fn on each value of collection and flattening the results.

Equivalent to using map followed by flatten, for simplicity and improved performance.

mapFlat<VFrom, VTo>(
  collection: Collection<VFrom>,
  fn: VFrom => Collection<VTo>,
): $Set<VTo>
St.mapFlat([1, 2], x => [- 1, x]) // $St(0, 1, 2)

Source Tests

Divide

St.remove

Create a Set with value removed.

remove<V>(collection: Collection<V>, value: V): $Set<V>
St.remove($St(1, 2, 3), 1) // $St(2, 3)
St.remove($St(1, 2, 3), 4) // $St(1, 2, 3)

Source Tests

St.chunk

Create an array of Sets which are chunks of given collection of given size.

If the collection doesn't divide evenly, the final chunk will be smaller than the rest. The collection will be first converted to a Set.

chunk<V>(
  collection: Collection<V>,
  size: number,
): $Array<$Set<V>>
St.chunk([4, 2, 3, 4, 5, 6], 2) // [$St(4, 2), $St(3, 5), $St(6)]

Source Tests

St.partition

Create a tuple of Sets containing items of collection which match and don't match predicateFn respectively.

More effecient than using multiple St.filter calls.

partition<K, V>(
  collection: KeyedCollection<K, V>,
  predicateFn: (V, K) => boolean,
): [$Set<V>, $Set<V>]
St.partition([1, 2, 3, 2], x => Mth.isEven(x)) // [$Set(2), $Set(1, 3)]

Source Tests

Str (string)

Work in progress

Check

Str.isString

Returns whether given value is a string.

isString(value: mixed): bool
Str.isString("hello") // true

Source

Construct

Str.fromNumber

Create the string representation of number, with given number of decimals, which defaults to 0, and optionally using a different decimalPoint and adding a thousandsSeparator.

For a string represention that shows all present decimals use `String(number)`.

fromNumber(
  number: number,
  decimals?: number = 0,
  decimalPoint?: string = '.',
  thousandsSeparator?: string = '',
): string
Str.fromNumber(1234.56) // '1234'
Str.fromNumber(1234.56, 1) // '1234.6'
Str.fromNumber(1234.56, 4) // '1234.5600'
Str.fromNumber(1234.56, 2, ',', '.') // '1.234,56'

Source

Str.toNumber

Return the number represented by given string.

toNumber(
  string: string,
  decimalPoint?: string = '.',
  thousandsSeparator?: string = '',
): number
Str.toNumber('1234.56') // 1234.56
Str.fromNumber('1.234,56', ',', '.') // 1234.56

Source Tests

Str.fromNumberInLocale

Create the string representation of number based on the execution environment.

See developer.mozilla.org/.../NumberFormat for full details.

There is currently no counter part, ie no Str.toNumberFromLocale, because there is no such API in JavaScript.

fromNumberInLocale(
  number: number,
  locales?: string | Array<string>,
  options?: Intl$NumberFormatOptions,
): string

Source

Str.repeat

Create a new string by repeating string count times.

repeat(string: string, count: number): string
Str.repeat('abr', 3) // 'abrabrabr'

Source Tests

Str.fill

Create a new string by concatenating count results of calling fn.

fn takeFirst as the first argument the position in the final string where the current invocation's result will be placed.

fill(times: number, fn: number => string): string
Str.fill(4, i => `${i}`) // '1234'

Source

Checks

Str.isEmpty

Returns true when string has zero length.

isEmpty(string: string): boolean
Str.isEmpty('') // true
Str.isEmpty('a') // false

Source

Str.length

Returns the length of the string.

length(string: string): number
Str.length('') // 0
Str.length('aa') // 2

Source

Str.startsWith

Returns whether string begins with given prefix.

startsWith(string: string, prefix: string | RegExp): boolean
Str.startsWith('adam', 'ad') // true
Str.startsWith('adam', /\w{2}/) // true

Source

Str.endsWith

Returns whether string ends with given suffix.

endsWith(string: string, suffix: string | RegExp)
Str.endsWith('adam', 'am') // true
Str.endsWith('adam', /m|M/) // true

Source

Str.includes

Returns whether string includes search.

If fromIndex is given then the occurence must be at or after it.

includes(
  string: string,
  search: string | RegExp,
  fromIndex?: number,
): boolean
Str.includes("abcd", "c") // true
Str.includes("ab cd", /\s/) // true

Source

Str.equals

Returns whether string equals given search.

equals(string: string, search: string | RegExp): boolean
Str.equals("abcd", "abcd") // true
Str.equals("abcd", /abcd/) // true
Str.equals("abcde", /abcd/) // false

Source

Str.indexOf

Returns the index of the first occurence of search in string or null.

If fromIndex is given then the occurence must be at or after it.

indexOf(
  string: string,
  search: string | RegExp,
  fromIndex?: number,
): ?number
Str.indexOf("abcd", "c") // 2
Str.indexOf("ab cd", "/\s/") // 2

Source

Str.lastIndexOf

Returns the index of the last occurence of search in string or null.

If fromIndex is given then the occurence must be at or after it.

lastIndexOf(
  string: string,
  search: string,
  fromIndex?: number,
)
Str.lastIndexOf("cbcd", "c") // 2

Source

Str.ignoreCase

Returns whether given predicateFn returns true for string and search when letter casing is ignored.

ignoreCase(
  string: string,
  search: string | RegExp,
  predicateFn: (string: string, search: string | RegExp) => boolean,
): boolean
Str.ignoreCase("abcd", "AbCD", Str.equals) // true
Str.ignoreCase("abcd", "AbCD", Str.equals) // true

Source Tests

Str.countMatches

Returns the number of times search occurs in string.

countMatches(string: string, search: string | RegExp): number
Str.countMatches("abcd", "c") // 1
Str.countMatches("ab cd", /\s/) // 1

Source

Str.compare

Work in progress

compare() {}

Source

Select

Str.matchEvery

Returns every match corresponding to search that occurs in string.

See Str.matchFirst for the description of the values in the returned array.

matchEvery(
  string: string,
  search: string | RegExp,
): $Array<RegExp$matchResult>
Str.matchEvery("a b1cd", /\w+/) // [['a'], ['b'], ['cd']]

Source

Str.matchFirst

Returns the first match corresponding to search that occurs in string or null.

If a matching substring is present the result will be an array where the first element is the full matching substring followed by smaller substrings corresponding to any matching regex groups. The array will have the property index which is the index at which the match occured in string and the property groups which will contain an object mapping named regex groups to the matching substrings, or undefined if there were no named groups in search.

If search is a regex with the g flag the flag will be ignored.

matchFirst(
  string: string,
  search: string | RegExp,
): ?RegExp$matchResult
Str.matchFirst("apple", /\w+(pp)?/) // ['app', 'pp'] {index: 0}

Source

Str.slice

Returns a substring of string starting with character at position startIndex and ending before character at position endIndex or at the end of string if no endIndex is given.

Either index can be negative, in which case they are counted from the end of the string, with last character being at index `-1`.

slice(
  string: string,
  startIndex: number,
  endIndex?: number,
): string
Str.slice('abcdef', 1, 2) // 'bc'
Str.slice('abcdef', -3, -1) // 'de'

Source

Str.takeFirst

Returns the first n characters in string.

Throws if n is negative.

takeFirst(string: string, n: number): string
Str.takeFirst('abcdef', 3) // 'abc'

Source

Str.dropFirst

Returns the last n characters in string.

Throws if n is negative.

dropFirst(string: string, n: number): string
Str.dropFirst('abcdef', 3) // 'def'

Source

Str.takeFirstWhile

Returns all characters from string preceding the character for which predicateFn returns false.

takeFirstWhile(
  string: string,
  predicateFn: string => boolean,
): string
Str.takeFirstWhile('abcdefg', (ch) => ch < 'd') // 'abc'

Source

Str.dropFirstWhile

Returns all characters from string following and including the first character for which predicateFn returns true.

dropFirstWhile(
  string: string,
  predicateFn: string => boolean,
): string
Str.dropFirstWhile('abcdefg', (ch) => ch < 'd') // 'defg'

Source

Str.trim

Returns string with whitespace stripped from the beginning and end.

If search is given it will be stripped instead from both ends. The beginning will be trimmed first.

trim(string: string, search?: string | RegExp): string
Str.trim('  abc    ') // 'abc'
Str.trim('hellodollyhello', 'hello') // 'dolly'
Str.trim('anana', 'ana') // 'na'
Str.trim('ya832da', /\w+/) // '832'

Source Tests

Str.trimStart

Returns string with whitespace stripped from its beginning.

If prefix is given it will be stripped instead.

trimStart(string: string, prefix?: string | RegExp): string
Str.trimStart('  abc    ') // 'abc    '
Str.trimStart('hellodollyhello', 'hello') // 'dollyhello'
Str.trimStart('ya832da', /\w+/) // '832da'

Source Tests

Str.trimEnd

Returns string with whitespace stripped from its end.

If suffix is given it will be stripped instead.

trimEnd(string: string, suffix?: string | RegExp): string
Str.trimEnd('  abc    ') // '  abc'
Str.trimEnd('hellodollyhello', 'hello') // 'hellodolly'
Str.trimEnd('ya832da', /\w+/) // 'ya832'

Source Tests

Combine

Str.join

TODO:

join(collection: Collection<string>, glue: string): string

Source

Str.joinChars

Work in progress

joinChars(chars: Collection<string>)

Source

Str.joinWords

Work in progress

joinWords(words: Collection<string>)

Source

Str.joinLines

Work in progress

joinLines(lines: Collection<string>)

Source

Divide

Str.split

Work in progress

split(
  string: string,
  delimiter: string | RegExp,
  limit?: number,
): $Array<string>

Source

Str.splitAt

Create a tuple of strings containing the first n characters and all but the first n characters of given string.

splitAt(string: string, n: number): [string, string]
Str.split("hello", 2) // ["he", "llo"]

Source

Str.chunk

Work in progress

chunk()

Source

Str.chunkFromEnd

Work in progress

chunkFromEnd(string: string, size: number)

Source

Str.splitChars

TODO:

splitChars(string: string): $Array<string>

Source

Str.splitWords

Work in progress

splitWords(string: string): $Array<string>

Source Tests

Str.splitLines

Work in progress

splitLines(
  string: string,
  ignoreTrailingNewLine?: boolean = false,
): $Array<string>

Source Tests

Str.span

Work in progress

span() {}
// TODO: 210
export function splice() {}

Source

Transform

Str.forEachCodePoint

Work in progress

forEachCodePoint(
  string: string,
  fn: (string, number) => boolean,
): void

Source

Str.replaceEvery

TODO

replaceEvery(
  string: string,
  search: string | RegExp,
  replacement:
    | string
    | ((match: string, ...groupsAndOffset: Array<any>) => string),
)

Source Tests

Str.replaceFirst

TODO

replaceFirst(
  string: string,
  search: string | RegExp,
  replacement:
    | string
    | ((match: string, ...groupsAndOffset: Array<any>) => string),
)

Source

Str.replaceFirstCaseIgnored

Work in progress

replaceFirstCaseIgnored() {}
// TODO: 1337
export function replaceEveryCaseIgnored() {}
// TODO:
export function camelize()

Source

Str.capitalize

Work in progress

capitalize(string: string): string

Source

Str.capitalizeWords

Work in progress

capitalizeWords() {}
// TODO:
export function dasherize() {}
// TODO:
export function underscorize() {}
// TODO: 15578
export function lowercase(string: string): string

Source

Str.uppercase

Work in progress

uppercase() {}
// TODO: 420
export function padStart() {}
// TODO: 317
export function padEnd() {}

Source