Skip to main content

Froebel - a strictly typed TypeScript utility library.

This is my (WIP) personal collection of TypeScript helper functions and utilities that I use across different projects.

Think an opionated version of lodash, but with first-class types.

Runs in Deno, Node.js, and the Browser. Install from deno.land or npm.

Function

ident

(value: T) => T

source

Identity function.

import

Node

import ident from "froebel/ident";

Deno

import ident from "https://deno.land/x/froebel@v0.14.3/ident.ts";

partial

(fun: T, ...argsLeft: PL) => (...argsRight: PR) => ReturnType<T>

source

Partially apply a function.

import

Node

import partial from "froebel/partial";

Deno

import partial from "https://deno.land/x/froebel@v0.14.3/partial.ts";

Example

const divide = (dividend: number, divisor: number) => dividend / divisor

// (divisor: number) => number
const oneOver = partial(divide, 1)

// prints: 0.25
console.log(oneOver(4))

forward

(fun: T, ...argsRight: PR) => (...argsLeft: PL) => ReturnType<T>

source

Given a function and its nth..last arguments, return a function accepting arguments 0..n-1.

import

Node

import forward from "froebel/forward";

Deno

import forward from "https://deno.land/x/froebel@v0.14.3/forward.ts";

Examples

const divide = (dividend: number, divisor: number) => dividend / divisor

// (dividend: number) => number
const divideBy2 = partial(divide, 2)

// prints: 0.5
console.log(divideBy2(1))
const fetchUrl = async (protocol: string, domain: string, path: string) =>
  await fetch(`${protocol}://${domain}/${path}`)

const fetchRepo = forward(fetchUrl, 'github.com', 'MathisBullinger/froebel')

const viaHTTPS = await fetchRepo('https')

callAll

(funs: F[], ...args: P) => ReturnTypes<F>

source

Take a list of functions that accept the same parameters and call them all with the provided arguments.

import

Node

import callAll from "froebel/callAll";

Deno

import callAll from "https://deno.land/x/froebel@v0.14.3/callAll.ts";

Example

const mult = (a: number, b: number) => a * b
const div  = (a: number, b: number) => a / b

// prints: [8, 2]
console.log( callAll([mult, div], 4, 2) )

bundle

(...funs: λ<T>[]) => (...args: T) => Promise<void>

source

Given a list of functions that accept the same parameters, returns a function that takes these parameters and invokes all of the given functions.

The returned function returns a promise that resolves once all functions returned/resolved and rejects if any of the functions throws/rejects - but only after all returned promises have been settled.

import

Node

import bundle from "froebel/bundle";

Deno

import bundle from "https://deno.land/x/froebel@v0.14.3/bundle.ts";

bundleSync

(...funs: λ<T>[]) => (...args: T) => void

source

Same as bundle, but return synchronously.

If any of the functions throws an error synchronously, none of the functions after it will be invoked and the error will propagate.

import

Node

import { bundleSync } from "froebel/bundle";

Deno

import { bundleSync } from "https://deno.land/x/froebel@v0.14.3/bundle.ts";

nullishChain

(...funs: [] | [FF, ...FR[]]) => (...args: Parameters<FF>) => ReturnType<FF> | ReturnType<FR[number]>

source

Given a list of functions that accept the same parameters, returns a function that given these arguments returns the result of the first function whose result is not nullish.

This is equivalent to chaining together invocations of the passed in functions with the given arguments with nullish coalescing (??) operators.

import

Node

import { nullishChain } from "froebel/nullishChain";

Deno

import { nullishChain } from "https://deno.land/x/froebel@v0.14.3/nullishChain.ts";

Example

const isAdult   = (age: number) => { if (n >= 18) return 'adult' }
const isToddler = (age: number) => { if (n <= 3) return 'toddler' }

const ageGroup = nullishChain(isAdult, isToddler, () => 'child')

// this is functionally equivalent to:
const ageGroup = age => isAdult(age) ?? isToddler(age) ?? 'child'

ageGroup(1)  // prints: 'toddler'
ageGroup(10) // prints: 'child'
ageGroup(50) // prints: 'adult'

asyncNullishChain

(...funs: [] | [FF, ...FR[]]) => (...args: Parameters<FF>) => Promise<PromType<ReturnType<FF>> | PromType<ReturnType<FR[number]>>>

source

Same as nullishChain but accept asynchronous functions too.

import

Node

import { asyncNullishChain } from "froebel/nullishChain";

Deno

import { asyncNullishChain } from "https://deno.land/x/froebel@v0.14.3/nullishChain.ts";

Example

const readFromCache = (id: string): Resource => { if (id in cache) return cache[id] }
const readFromFile  = (id: string): Resource => { if (fileExists(id)) return readFile(id) }
const fetchFromNet  = async (id: string): Promise<Resource> => await fetch(`someURL/${id}`)

// async (id: string) => Promise<Resource>
const getResource = asyncNullishChain(readFromCache, readFromFile, fetchFromNet)

throttle

(fun: T, ms: number, opts?: {leading: boolean, trailing: boolean}) => λ<Parameters<T>, void> & {[cancel]: () => void}

source

Created a throttled function that invokes fun at most every ms milliseconds.

fun is invoked with the last arguments passed to the throttled function.

Calling [throttle.cancel]() on the throttled function will cancel the currently scheduled invocation.

import

Node

import throttle from "froebel/throttle";

Deno

import throttle from "https://deno.land/x/froebel@v0.14.3/throttle.ts";

debounce

(fun: T, ms: number) => λ<Parameters<T>, void> & {[cancel]: () => void}

source

Creates a debounced function that delays invoking fun until ms milliseconds have passed since the last invocation of the debounced function.

fun is invoked with the last arguments passed to the debounced function.

Calling [debounce.cancel]() on the debounced function will cancel the currently scheduled invocation.

import

Node

import debounce from "froebel/debounce";

Deno

import debounce from "https://deno.land/x/froebel@v0.14.3/debounce.ts";

memoize

(fun: T, opt: {limit: number, weak: W, key: (...args: Parameters<T>) => K}) => T & {cache: W extends false ? Map<K, ReturnType<T>> : Cache<K, ReturnType<T>>}

source

Returns a copy of fun that remembers its result for any given arguments and only invokes fun for unknown arguments.

The cache key is computed using the key function. The default key function simply stringifies the arguments.

If limit is specified, only the limit-last entries are kept in cache.

The function’s cache is available at memoized.cache.

If opt.weak is true, non-primitive cache keys are stored in a WeakMap. This behavior might for example be useful if you want to memoize some calculation including a DOM Node without holding on to a reference of that node. Using weak keys prohibits setting a limit.

import

Node

import memoize from "froebel/memoize";

Deno

import memoize from "https://deno.land/x/froebel@v0.14.3/memoize.ts";

Examples

const expensiveCalculation = (a: number, b: number) => {
  console.log(`calculate ${a} + ${b}`)
  return a + b
}
const calc = memoize(expensiveCalculation)

console.log( calc(1, 2) )
// calculate 1 + 2
// 3
console.log( calc(20, 5) )
// calculate 20 + 5
// 25
console.log( calc(20, 5) )
// 25
console.log( calc(1, 2) )
// 3

calc.cache.clear()
console.log( calc(1, 2) )
// calculate 1 + 2
// 3
const logIfDifferent = memoize(
  (msg: string) => console.log(msg),
  {
    limit: 1,
    key: msg => msg
  }
)

logIfDifferent('a')
logIfDifferent('a')
logIfDifferent('b')
logIfDifferent('a')

// a
// b
// a

limitInvocations

(fun: T, limit: number, ...funs: ExcS<T>) => T

source

Returns a version of the function fun that can only be invoked limit times. An optional except function will be called with the same parameters on any additional invocations.

If fun returns anything but void (or Promise<void>), supplying an except function is mandatory.

The except function must have the same return type as fun, or — if fun returns a promise — it may return the type that the promise resolves to synchronously.

The except function may also throw instead of returning a value.

import

Node

import { limitInvocations } from "froebel/invoke";

Deno

import { limitInvocations } from "https://deno.land/x/froebel@v0.14.3/invoke.ts";

once

(fun: T, ...funs: ExcS<T>) => T

source

Special case of limitInvocations. fun can only be invoked once.

see limitInvocations

import

Node

import { once } from "froebel/invoke";

Deno

import { once } from "https://deno.land/x/froebel@v0.14.3/invoke.ts";

List

atWrap

(arr: T[], i: number) => T

source

Access list at i % length. Negative indexes start indexing the last element as [-1] and wrap around to the back.

import

Node

import atWrap from "froebel/atWrap";

Deno

import atWrap from "https://deno.land/x/froebel@v0.14.3/atWrap.ts";

zip

(...lists: T) => Zip<T>

source

Takes multiple lists and returns a list of tuples containing the value in each list at the current index. If the lists are of different lengths, the returned list of tuples has the length of the shortest passed in list.

import

Node

import zip from "froebel/zip";

Deno

import zip from "https://deno.land/x/froebel@v0.14.3/zip.ts";

Example

const pairs = zip([1,2,3], ['a','b','c'])
console.log(pairs) // prints: [[1,'a'], [2,'b'], [3,'c']]

zipWith

(zipper: (...args: {[I in string | number | symbol]: U}) => U, ...lists: T) => U[]

source

Same as zip but also takes a zipper function, that is called for each index with the element at current index in each list as arguments. The result of zipper is the element at current index in the list returned from zipWith.

import

Node

import { zipWith } from "froebel/zip";

Deno

import { zipWith } from "https://deno.land/x/froebel@v0.14.3/zip.ts";

Example

const sums = zipWith((a,b) => a+b, [1,2,3], [4,5,6])
console.log(sums) // prints: [5,7,9]

unzip

(...zipped: T[][]) => Unzip<T>

source

Reverse of zip. Takes a list of tuples and deconstructs them into an array (of length of the tuples length) of lists each containing all the elements in all tuples at the lists index.

import

Node

import unzip from "froebel/unzip";

Deno

import unzip from "https://deno.land/x/froebel@v0.14.3/unzip.ts";

Example

const [nums, chars] = unzip([1,'a'], [2,'b'], [3,'c'])
console.log(nums)  // prints: [1, 2, 3]
console.log(chars) // prints: ['a','b','c']

unzipWith

(zipped: T[][], ...unzippers: U) => {[I in string | number | symbol]: ReturnType<U[I]>}

source

Same as unzip but accepts an unzipper function for each tuple index. The unzipper’s return value is used as the value in the list at that index returned from unzipWith.

The unzipper takes the current element as its first argument, an accumulator as second argument (initially undefined) and its return value is the accumulator passed into the next invocation.

import

Node

import { unzipWith } from "froebel/unzip";

Deno

import { unzipWith } from "https://deno.land/x/froebel@v0.14.3/unzip.ts";

Example

const [nums, str] = unzip(
  [ [1,'a'], [2,'b'], [3,'c'] ],
  (n, acc: number[] = []) => [...acc, n],
  (c, str = '') => str + c
)

console.log(nums) // prints: [1, 2, 3]
console.log(str)  // prints: 'abc'

batch

(list: T[], batchSize: number) => T[][]

source

Takes a list and returns it in multiple smaller lists of the size batchSize. The last batch may be smaller than batchSize depending on if list size is divisible by batchSize.

import

Node

import batch from "froebel/batch";

Deno

import batch from "https://deno.land/x/froebel@v0.14.3/batch.ts";

Example

batch([1,2,3,4,5], 2)  // -> [ [1,2], [3,4], [5] ]

partition

(list: T[], predicate: (el: T) => el is S) => [S[], Exclude<T, S>[]]

source

Takes a list and returns a pair of lists containing: the elements that match the predicate and those that don’t, respectively.

Think of it as filter, but the elements that don’t pass the filter aren’t discarded but returned in a separate list instead.

import

Node

import partition from "froebel/partition";

Deno

import partition from "https://deno.land/x/froebel@v0.14.3/partition.ts";

Example

const [strings, numbers] = partition(
  ['a', 'b', 1, 'c', 2, 3],
  (el): el is string => typeof el === 'string'
)
// strings: ["a", "b", "c"]
// numbers: [1, 2, 3]

take

(n: number, list: Iterable<T>) => T[]

source

Takes n elements from the iterable list and returns them as an array.

import

Node

import { take } from "froebel/take";

Deno

import { take } from "https://deno.land/x/froebel@v0.14.3/take.ts";

Example

take(5, repeat(1, 2))  // -> [1, 2, 1, 2, 1]
take(3, [1, 2, 3, 4])  // -> [1, 2, 3]
take(3, [1, 2])        // -> [1, 2]

range

source

Creates a range between two values.

see numberRange and alphaRange

import

Node

import range from "froebel/range";

Deno

import range from "https://deno.land/x/froebel@v0.14.3/range.ts";

numberRange

(start: number, end: number, step: number) => number[]

source

Constructs a numeric between start and end inclusively.

import

Node

import { numberRange } from "froebel/range";

Deno

import { numberRange } from "https://deno.land/x/froebel@v0.14.3/range.ts";

Example

range(2, 6)      // -> [2, 3, 4, 5, 6]
range(8, 9, .3)  // -> [8, 8.3, 8.6, 8.9]
range(3, -2)     // -> [3, 2, 1, 0, -1, -2]

alphaRange

(start: string, end: string) => string[]

source

Constructs a range between characters.

import

Node

import { alphaRange } from "froebel/range";

Deno

import { alphaRange } from "https://deno.land/x/froebel@v0.14.3/range.ts";

Example

range('a', 'd')  // -> ['a', 'b', 'c', 'd']
range('Z', 'W')  // -> ['Z', 'Y', 'X', 'W']

Iterable

repeat

(...sequence: [T, ...T[]]) => Generator<T>

source

Returns a generator that repeats sequence.

import

Node

import repeat from "froebel/repeat";

Deno

import repeat from "https://deno.land/x/froebel@v0.14.3/repeat.ts";

Example

// prints: 1, 2, 3, 1, 2, 3, ...
for (const n of repeat(1, 2, 3))
  console.log(n)

take

(n: number, list: Iterable<T>) => Generator<T>

source

Takes n elements from the iterable list and returns them as a generator.

import

Node

import { take } from "froebel/take";

Deno

import { take } from "https://deno.land/x/froebel@v0.14.3/take.ts";

Example

[...take(5, repeat(1, 2))]  // -> [1, 2, 1, 2, 1]
[...take(3, [1, 2, 3, 4])]  // -> [1, 2, 3]
[...take(3, [1, 2])]        // -> [1, 2]

Object

pick

(obj: T, ...keys: K[]) => Pick<T, K>

source

From obj, create a new object that only includes keys.

import

Node

import pick from "froebel/pick";

Deno

import pick from "https://deno.land/x/froebel@v0.14.3/pick.ts";

Example

pick({ a: 1, b: 2, c: 3 }, 'a', 'c') // { a: 1, c: 3 }

omit

(obj: T, ...keys: K[]) => Omit<T, K>

source

From obj, create a new object that does not include keys.

import

Node

import omit from "froebel/omit";

Deno

import omit from "https://deno.land/x/froebel@v0.14.3/omit.ts";

Example

omit({ a: 1, b: 2, c: 3 }, 'a', 'c') // { b: 2 }

Equality

oneOf

(value: T, ...cmps: TT) => value is TT[number]

source

Checks if v is one of cmps.

import

Node

import oneOf from "froebel/oneOf";

Deno

import oneOf from "https://deno.land/x/froebel@v0.14.3/oneOf.ts";

equal

(a: unknown, b: unknown) => boolean

source

Checks if a and b are structurally equal using the following algorithm:

  • primitives are compared by value
  • functions are compared by reference
  • objects (including arrays) are checked to have the same properties and their values are compared recursively using the same algorithm
import

Node

import equal from "froebel/equal";

Deno

import equal from "https://deno.land/x/froebel@v0.14.3/equal.ts";

clone

(value: T) => T

source

Returns a copied version of value.

If value is primitive, returns value. Otherwise, properties of value are copied recursively. Only value’s own enumerable properties are cloned. Arrays are cloned by mapping over their elements.

If a path in value references itself or a parent path, then in the resulting object that path will also reference the path it referenced in the original object (but now in the resuling object instead of the original).

import

Node

import clone from "froebel/clone";

Deno

import clone from "https://deno.land/x/froebel@v0.14.3/clone.ts";

Promise

isPromise

(value: unknown) => value is Promise<T>

source

Checks if value looks like a promise.

import

Node

import isPromise from "froebel/isPromise";

Deno

import isPromise from "https://deno.land/x/froebel@v0.14.3/isPromise.ts";

isNotPromise

(value: T) => value is Exclude<T, Promise<any>>

source

Checks if value is not a promise.

import

Node

import { isNotPromise } from "froebel/isPromise";

Deno

import { isNotPromise } from "https://deno.land/x/froebel@v0.14.3/isPromise.ts";

Example

(value: number | Promise<unknown>) => {
  if (isNotPromise(value)) return value / 2
}

Predicate

truthy

(value: T) => value is PickTruthy<T>

source

Checks if value is truthy. Literal types are narrowed accordingly.

import

Node

import { truthy } from "froebel/truthy";

Deno

import { truthy } from "https://deno.land/x/froebel@v0.14.3/truthy.ts";

falsy

(value: T) => value is PickFalsy<T>

source

Checks if value is falsy. Literal types are narrowed accordingly.

import

Node

import { falsy } from "froebel/truthy";

Deno

import { falsy } from "https://deno.land/x/froebel@v0.14.3/truthy.ts";

nullish

(value: T) => value is Nullish<T>

source

Checks if value is nullish. Literal types are narrowed accordingly.

import

Node

import { nullish } from "froebel/nullish";

Deno

import { nullish } from "https://deno.land/x/froebel@v0.14.3/nullish.ts";

notNullish

(value: null | T) => value is T

source

Checks if value is not nullish. Literal types are narrowed accordingly.

import

Node

import { notNullish } from "froebel/nullish";

Deno

import { notNullish } from "https://deno.land/x/froebel@v0.14.3/nullish.ts";

Example

const nums = (...values: (number | undefined)[]): number[] => values.filter(notNullish)

isFulfilled

(result: PromiseSettledResult<T>) => result is PromiseFulfilledResult<T>

source

Checks if result (returned from Promise.allSettled) is fulfilled.

import

Node

import { isFulfilled } from "froebel/settled";

Deno

import { isFulfilled } from "https://deno.land/x/froebel@v0.14.3/settled.ts";

isRejected

(result: PromiseSettledResult<unknown>) => result is PromiseRejectedResult

source

Checks if result (returned from Promise.allSettled) is rejected.

import

Node

import { isRejected } from "froebel/settled";

Deno

import { isRejected } from "https://deno.land/x/froebel@v0.14.3/settled.ts";

String

prefix

(prefix: T0, str: T1, caseMod?: C) => `${string}`

source

Returns str prefixed with prefix. Optionally, allows prefxing in camel case, i.e. prefix('foo', 'bar', 'camel') => 'fooBar', or snake case, i.e. prefix('foo', 'bar', 'snake') => 'foo_bar'.

The result is strictly typed, so prefix('foo', 'bar') will return the type 'foobar', not just a generic string.

import

Node

import prefix from "froebel/prefix";

Deno

import prefix from "https://deno.land/x/froebel@v0.14.3/prefix.ts";

suffix

(str: T1, suffix: T0, caseMod?: C) => `${string}`

source

Returns str suffixed with suffix. Same case and type behavior as prefix.

import

Node

import suffix from "froebel/suffix";

Deno

import suffix from "https://deno.land/x/froebel@v0.14.3/suffix.ts";

capitalize

(str: T) => Capitalize

source

Upper-case first letter of string.

import

Node

import { capitalize } from "froebel/case";

Deno

import { capitalize } from "https://deno.land/x/froebel@v0.14.3/case.ts";

uncapitalize

(str: T) => Uncapitalize

source

Lower-case first letter of string

import

Node

import { uncapitalize } from "froebel/case";

Deno

import { uncapitalize } from "https://deno.land/x/froebel@v0.14.3/case.ts";

upper

(str: T) => Uppercase

source

Strictly typed String.toUpperCase().

import

Node

import { upper } from "froebel/case";

Deno

import { upper } from "https://deno.land/x/froebel@v0.14.3/case.ts";

lower

(str: T) => Lowercase

source

Strictly typed String.toLowerCase().

import

Node

import { lower } from "froebel/case";

Deno

import { lower } from "https://deno.land/x/froebel@v0.14.3/case.ts";

snake

(str: T) => SnakeCase<T>

source

Transforms a variable name to snake case.

Note: The rules for transforming anything to snake case are somewhat vague. So use this only for very simple names where the resulting value is absolutely unambiguous. For more examples of how names are transformed, have a look at the test cases.

import

Node

import { snake } from "froebel/case";

Deno

import { snake } from "https://deno.land/x/froebel@v0.14.3/case.ts";

Example

snake('fooBar') // 'foo_bar'

camel

(str: T) => CamelCase<T>

source

Transforms a variable name to camel case.

Note: The rules for transforming anything to camel case are somewhat vague. So use this only for very simple names where the resulting value is absolutely unambiguous. For more examples of how names are transformed, have a look at the test cases.

import

Node

import { camel } from "froebel/case";

Deno

import { camel } from "https://deno.land/x/froebel@v0.14.3/case.ts";

Example

camel('foo_bar') // 'fooBar'

transformCase

(str: T, targetCase: C) => SnakeCase<T>

source

Transform a variable name to targetCase

see snake and camel

import

Node

import { transformCase } from "froebel/case";

Deno

import { transformCase } from "https://deno.land/x/froebel@v0.14.3/case.ts";

Math

clamp

(min: number, num: number, max: number) => number

source

Clamp num between min and max inclusively.

import

Node

import clamp from "froebel/clamp";

Deno

import clamp from "https://deno.land/x/froebel@v0.14.3/clamp.ts";

Data Structures

BiMap

class BiMap<L, R>(data?: Map<L, R> | [L, R][], aliasLeft?: AL, aliasRight?: AR)

source

Bidirectional map. Maps two sets of keys in a one-to-one relation.

Both sides are accessible (at .left & .right, or at their respective alias if one was provided in the constructor) with an interface similar to that of the built-in Map and the same iteration behavior.

import

Node

import BiMap from "froebel/bimap";

Deno

import BiMap from "https://deno.land/x/froebel@v0.14.3/bimap.ts";

Examples

const nums = BiMap.from({ one: 1, two: 2 })

// different ways of iterating over the entries
[...nums.left]                 // [['one',1], ['two',2]]
[...nums.right]                // [[1,'one'], [2,'two']]
[...nums.left.keys()]          // ['one', 'two']
[...nums.left.values()]        // [1, 2]
[...nums.right.keys()]         // [1, 2]
[...nums.right.values()]       // ['one', 'two']
[...nums]                      // [['one',1], ['two',2]]
[...nums.right.entries()]      // [[1,'one'], [2,'two']]
Object.fromEntries(nums.right) // { '1': 'one', '2': 'two' }

// setting a value
nums.left.three = 3
// when accessing a property using bracket notation (i.e. nums.right[4]),
// JavaScript coerces the key to a string, so keys that aren't strings or
// symbols must be accessed using the same access methods known from Map.
nums.right.set(4, 'four')

// remapping values
nums.left.tres = 3          // {one: 1, two: 2, tres: 3, four: 4}
nums.right.set(4, 'cuatro') // {one: 1, two: 2, tres: 3, cuatro: 4}

// deleting
delete nums.left.tres    // {one: 1, two: 2, cuatro: 4}
nums.right.delete(4)     // {one: 1, two: 2}

// reversing the map
const num2Name = nums.reverse()
console.log([...num2Name.left])                 // [[1,'one'], [2,'two']]
console.log(Object.fromEntries(num2Name.right)) // {one: 1, two: 2}

// other methods known from built-in Map
nums.size               // 2
nums.[left|right].size  // 2
nums.clear() // equivalent to nums.[left|right].clear()
console.log(nums.size)  // 0
// giving aliases to both sides
const dictionary = new BiMap(
  [
    ['hello', 'hallo'],
    ['bye', 'tschüss'],
  ],
  'en',
  'de'
)

dictionary.de.get('hallo') // 'hello'
dictionary.en.get('bye')   // 'tschüss'

delete dictionary.de.hallo
console.log(Object.fromEntries(dictionary.en)) // { bye: 'tschüss' }

// you can also use the BiMap.alias method:
BiMap.alias('en', 'de')<string, string>()
BiMap.alias('en', 'de')([['hello', 'hallo']])
BiMap.alias('en', 'de')(new Map<string, string>())
BiMap.alias('en', 'de')({ hello: 'hallo' })
BiMap.alias('en', 'de')(new Set(['hello']), new Set(['hallo']))

// the same arguments can be used with BiMap.from, e.g.:
BiMap.from(new Set<number>(), new Set<number>())

SortedArray

class SortedArray<T>(compare: Cmp<T>, ...value: T[])

source

Sorted array. Behaves much like a regular array but its elements remain sorted using the compare function supplied in the constructor.

Contains most of the methods defined on regular JavaScript arrays as long as they don’t modify the array’s content in place.

New elements are added using the add(...values) method.

Elements can still be accessed using bracket notation as in plain JavaScript arrays but can’t be assigned to using bracket notation (as that could change the element’s sort position).

Elements can be removed using the delete(...indices) method, which returns an array containing the deleted values. Deleting an element using delete sorted[index] will also work, but results in a TypeScript error because element access is marked readonly.

Array methods that pass a reference of the array to a callback (e.g. map, reduce, find) will pass a reference to the SortedArray instance instead.

The filter and slice methods will return SortedArray instances instead of plain arrays.

import

Node

import SortedArray from "froebel/sortedArray";

Deno

import SortedArray from "https://deno.land/x/froebel@v0.14.3/sortedArray.ts";

SortedMap

class SortedMap<K, V>(compare: Cmp<K, V>, entries?: null | [K, V][])

source

Behaves like a regular JavaScript Map, but its iteration order is dependant on the compare function supplied in the constructor.

Note: The item’s sort position is only computed automatically on insertion. If you update one of the values that the compare function depends on, you must call the update(key) method afterwards to ensure the map stays sorted.

import

Node

import SortedMap from "froebel/sortedMap";

Deno

import SortedMap from "https://deno.land/x/froebel@v0.14.3/sortedMap.ts";

Path

select

(obj: T, ...path: P) => PickPath<T, P>

source

Returns the value in obj at path. If the given path does not exist, the symbol none is returned.

import

Node

import select from "froebel/select";

Deno

import select from "https://deno.land/x/froebel@v0.14.3/select.ts";