Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/MathisBullinger/froebel

A strictly typed utility library.
https://github.com/MathisBullinger/froebel

deno functional-programming javascript typescript utility-library

Last synced: 2 days ago
JSON representation

A strictly typed utility library.

Awesome Lists containing this project

README

        

# 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 opinionated version of lodash, but with first-class types.

If you have an idea for a utility that might make a good addition to this collection,
please open an issue and suggest its inclusion.

Runs in Deno, Node.js, and the Browser. Get it from [deno.land](https://deno.land/x/[email protected])
or [npm](https://www.npmjs.com/package/froebel).

## Installation

### Using npm

```shell
npm install froebel
```

and — assuming a module-compatible system like webpack — import as:

```ts
import { someUtility } from 'froebel';
// you can also import the utility you need directly:
import memoize from 'froebel/memoize';
```

### Using Deno

```ts
import { someUtility } from "https://deno.land/x/[email protected]/mod.ts";
// or import just the utility you need:
import memoize from "https://deno.land/x/[email protected]/memoize.ts"
```

---

## Available Utilities

Each category also has a file exporting only the utilities in that category, so
if you want to only import utilities from one category, you could import them as

```ts
import { throttle, debounce } from "froebel/function";
```

A few utils are exported from multiple categories but will only be listed here
once. For example `isPromise` is exported from both the `promise` and the
`predicate` category.

### Table of Contents

- __`function`__
- [ident](#ident)
- [noop](#noop)
- [partial](#partial)
- [forward](#forward)
- [unary](#unary)
- [callAll](#callall)
- [pipe](#pipe)
- [applyPipe](#applypipe)
- [bundle](#bundle)
- [bundleSync](#bundlesync)
- [nullishChain](#nullishchain)
- [asyncNullishChain](#asyncnullishchain)
- [throttle](#throttle)
- [debounce](#debounce)
- [memoize](#memoize)
- [limitInvocations](#limitinvocations)
- [once](#once)
- __`list`__
- [atWrap](#atwrap)
- [zip](#zip)
- [zipWith](#zipwith)
- [unzip](#unzip)
- [unzipWith](#unzipwith)
- [batch](#batch)
- [partition](#partition)
- [shuffle](#shuffle)
- [shuffleInPlace](#shuffleinplace)
- [take](#take)
- [range](#range)
- [numberRange](#numberrange)
- [alphaRange](#alpharange)
- __`iterable`__
- [repeat](#repeat)
- [take](#take)
- __`object`__
- [pick](#pick)
- [omit](#omit)
- [map](#map)
- __`path`__
- [select](#select)
- __`equality`__
- [oneOf](#oneof)
- [equal](#equal)
- [clone](#clone)
- [merge](#merge)
- __`promise`__
- [promisify](#promisify)
- [createQueue](#createqueue)
- [isPromise](#ispromise)
- [isNotPromise](#isnotpromise)
- __`predicate`__
- [truthy](#truthy)
- [falsy](#falsy)
- [nullish](#nullish)
- [notNullish](#notnullish)
- [isFulfilled](#isfulfilled)
- [isRejected](#isrejected)
- __`string`__
- [prefix](#prefix)
- [suffix](#suffix)
- [surround](#surround)
- [capitalize](#capitalize)
- [uncapitalize](#uncapitalize)
- [upper](#upper)
- [lower](#lower)
- [snake](#snake)
- [kebab](#kebab)
- [camel](#camel)
- [pascal](#pascal)
- [screamingSnake](#screamingsnake)
- [transformCase](#transformcase)
- __`math`__
- [clamp](#clamp)
- __`data structures`__
- [BiMap](#bimap)
- [SortedArray](#sortedarray)
- [SortedMap](#sortedmap)

## Function

#### `ident`

```hs
(value: T) => T
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/ident.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/ident.test.ts)_

> Identity function.

#### Import

```ts
/* Node: */ import ident from "froebel/ident";
/* Deno: */ import ident from "https://deno.land/x/[email protected]/ident.ts";
```

---

#### `noop`

```hs
() => void
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/noop.ts#L1)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/noop.test.ts)_

#### Import

```ts
/* Node: */ import noop from "froebel/noop";
/* Deno: */ import noop from "https://deno.land/x/[email protected]/noop.ts";
```

---

#### `partial`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/partial.ts#L17)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/partial.test.ts)_

> Partially apply a function.

#### Import

```ts
/* Node: */ import partial from "froebel/partial";
/* Deno: */ import partial from "https://deno.land/x/[email protected]/partial.ts";
```

#### Example
```ts
const divide = (dividend: number, divisor: number) => dividend / divisor

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

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

---

#### `forward`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/forward.ts#L28)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/forward.test.ts)_

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

#### Import

```ts
/* Node: */ import forward from "froebel/forward";
/* Deno: */ import forward from "https://deno.land/x/[email protected]/forward.ts";
```

#### Examples
```ts
const divide = (dividend: number, divisor: number) => dividend / divisor

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

// prints: 0.5
console.log(divideBy2(1))
```

```ts
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')
```

---

#### `unary`

```hs
(fun: T) => Unary
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/unary.ts#L15)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/unary.test.ts)_

> Turns `fun` into a unary function (a function that only accepts one
> argument).
>
> Note: `fun` must accept at least one argument and must not require more than
> one argument.
>

#### Import

```ts
/* Node: */ import unary from "froebel/unary";
/* Deno: */ import unary from "https://deno.land/x/[email protected]/unary.ts";
```

#### Example
```ts
['1', '2', '3'].map(unary(parseInt)) // -> [1, 2, 3]
```

---

#### `callAll`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/callAll.ts#L16)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/callAll.test.ts)_

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

#### Import

```ts
/* Node: */ import callAll from "froebel/callAll";
/* Deno: */ import callAll from "https://deno.land/x/[email protected]/callAll.ts";
```

#### Example
```ts
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) )
```

---

#### `pipe`

```hs
(...funs: T) => PipedFun
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/pipe.ts#L27)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/pipe.test.ts)_

> Given a list of functions returns a function that will execute the given
> functions one after another, always passing the result of the previous
> function as an argument to the next function.
>
> If one of the given functions returns a promise, the promise will be resolved
> before being passed to the next function.
>

#### Import

```ts
/* Node: */ import pipe from "froebel/pipe";
/* Deno: */ import pipe from "https://deno.land/x/[email protected]/pipe.ts";
```

#### Example
```ts
const join = (...chars: string[]) => chars.join('')
pipe(join, parseInt)('1', '2', '3') // -> 123

const square = (n: number) => n ** 2

// this is equivalent to: square(square(square(2)))
pipe(square, square, square)(2) // -> 256

// also works with promises:
fetchNumber :: async () => Promise
pipe(fetchNumber, n => n.toString()) // async () => Promise
```

---

#### `applyPipe`

```hs
(arg: Parameters[0], ...funs: T) => CheckPipe, Parameters>, false>
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/pipe.ts#L57)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/pipe.test.ts)_

> Like `pipe` but takes an argument as its first parameter and invokes the pipe
> with it.
>
> Note: unlike in `pipe`, the first function of the pipe must take exactly one
> argument.
>
>
> see [pipe](#pipe)

#### Import

```ts
/* Node: */ import { applyPipe } from "froebel/pipe";
/* Deno: */ import { applyPipe } from "https://deno.land/x/[email protected]/pipe.ts";
```

#### Example
```ts
applyPipe(2, double, square, half) // -> 8
```

---

#### `bundle`

```hs
(...funs: λ[]) => (...args: T) => Promise
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/bundle.ts#L12)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/bundle.test.ts)_

> 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

```ts
/* Node: */ import bundle from "froebel/bundle";
/* Deno: */ import bundle from "https://deno.land/x/[email protected]/bundle.ts";
```

---

#### `bundleSync`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/bundle.ts#L29)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/bundle.test.ts)_

> Same as [bundle](#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

```ts
/* Node: */ import { bundleSync } from "froebel/bundle";
/* Deno: */ import { bundleSync } from "https://deno.land/x/[email protected]/bundle.ts";
```

---

#### `nullishChain`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/nullishChain.ts#L26)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/nullishChain.test.ts)_

> 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

```ts
/* Node: */ import { nullishChain } from "froebel/nullishChain";
/* Deno: */ import { nullishChain } from "https://deno.land/x/[email protected]/nullishChain.ts";
```

#### Example
```ts
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`

```hs
(...funs: [] | [FF, ...FR[]]) => (...args: Parameters) => Promise> | PromType>>
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/nullishChain.ts#L45)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/nullishChain.test.ts)_

> Same as [nullishChain](#nullishchain) but accept asynchronous functions too.

#### Import

```ts
/* Node: */ import { asyncNullishChain } from "froebel/nullishChain";
/* Deno: */ import { asyncNullishChain } from "https://deno.land/x/[email protected]/nullishChain.ts";
```

#### Example
```ts
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 => await fetch(`someURL/${id}`)

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

---

#### `throttle`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/throttle.ts#L14)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/throttle.test.ts)_

> Create 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

```ts
/* Node: */ import throttle from "froebel/throttle";
/* Deno: */ import throttle from "https://deno.land/x/[email protected]/throttle.ts";
```

---

#### `debounce`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/debounce.ts#L14)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/debounce.test.ts)_

> 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

```ts
/* Node: */ import debounce from "froebel/debounce";
/* Deno: */ import debounce from "https://deno.land/x/[email protected]/debounce.ts";
```

---

#### `memoize`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/memoize.ts#L70)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/memoize.test.ts)_

> 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

```ts
/* Node: */ import memoize from "froebel/memoize";
/* Deno: */ import memoize from "https://deno.land/x/[email protected]/memoize.ts";
```

#### Examples
```ts
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
```

```ts
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`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/invoke.ts#L19)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/invoke.test.ts)_

> 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`), 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

```ts
/* Node: */ import { limitInvocations } from "froebel/invoke";
/* Deno: */ import { limitInvocations } from "https://deno.land/x/[email protected]/invoke.ts";
```

---

#### `once`

```hs
(fun: T, ...funs: ExcS) => T
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/invoke.ts#L40)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/invoke.test.ts)_

> Special case of [limitInvocations](#limitinvocations). `fun` can only be invoked once.
>
> see [limitInvocations](#limitinvocations)

#### Import

```ts
/* Node: */ import { once } from "froebel/invoke";
/* Deno: */ import { once } from "https://deno.land/x/[email protected]/invoke.ts";
```

## List

#### `atWrap`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/atWrap.ts#L3)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/atWrap.test.ts)_

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

#### Import

```ts
/* Node: */ import atWrap from "froebel/atWrap";
/* Deno: */ import atWrap from "https://deno.land/x/[email protected]/atWrap.ts";
```

---

#### `zip`

```hs
(...lists: T) => Zip
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/zip.ts#L16)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/zip.test.ts)_

> 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

```ts
/* Node: */ import zip from "froebel/zip";
/* Deno: */ import zip from "https://deno.land/x/[email protected]/zip.ts";
```

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

---

#### `zipWith`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/zip.ts#L35)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/zip.test.ts)_

> Same as [zip](#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

```ts
/* Node: */ import { zipWith } from "froebel/zip";
/* Deno: */ import { zipWith } from "https://deno.land/x/[email protected]/zip.ts";
```

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

---

#### `unzip`

```hs
(...zipped: T[][]) => Unzip
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/unzip.ts#L15)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/unzip.test.ts)_

> Reverse of [zip](#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

```ts
/* Node: */ import unzip from "froebel/unzip";
/* Deno: */ import unzip from "https://deno.land/x/[email protected]/unzip.ts";
```

#### Example
```ts
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`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/unzip.ts#L39)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/unzip.test.ts)_

> Same as [unzip](#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

```ts
/* Node: */ import { unzipWith } from "froebel/unzip";
/* Deno: */ import { unzipWith } from "https://deno.land/x/[email protected]/unzip.ts";
```

#### Example
```ts
const [nums, str] = unzipWith(
[ [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`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/batch.ts#L14)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/batch.test.ts)_

> 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

```ts
/* Node: */ import batch from "froebel/batch";
/* Deno: */ import batch from "https://deno.land/x/[email protected]/batch.ts";
```

#### Example
```ts
batch([1,2,3,4,5], 2) // -> [ [1,2], [3,4], [5] ]
```

---

#### `partition`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/partition.ts#L30)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/partition.test.ts)_

> 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

```ts
/* Node: */ import partition from "froebel/partition";
/* Deno: */ import partition from "https://deno.land/x/[email protected]/partition.ts";
```

#### Example
```ts
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]
```

---

#### `shuffle`

```hs
(list: T[]) => T[]
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/shuffle.ts#L5)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/shuffle.test.ts)_

> Shuffles `list` using the Fisher-Yates shuffle algorithm.
> The original `list` is not modified and the shuffled list is returned.

#### Import

```ts
/* Node: */ import shuffle from "froebel/shuffle";
/* Deno: */ import shuffle from "https://deno.land/x/[email protected]/shuffle.ts";
```

---

#### `shuffleInPlace`

```hs
(list: unknown[]) => void
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/shuffle.ts#L16)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/shuffle.test.ts)_

> Same as [shuffle](#shuffle) but shuffles `list` in place.

#### Import

```ts
/* Node: */ import { shuffleInPlace } from "froebel/shuffle";
/* Deno: */ import { shuffleInPlace } from "https://deno.land/x/[email protected]/shuffle.ts";
```

---

#### `take`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/take.ts#L11)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/take.test.ts)_

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

#### Import

```ts
/* Node: */ import { take } from "froebel/list";
/* Deno: */ import { take } from "https://deno.land/x/[email protected]/list.ts";
```

#### Example
```ts
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](https://github.com/MathisBullinger/froebel/blob/main/range.ts#L66)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/range.test.ts)_

> Creates a range between two values.
>
> see [numberRange](#numberrange) and [alphaRange](#alpharange)

#### Import

```ts
/* Node: */ import range from "froebel/range";
/* Deno: */ import range from "https://deno.land/x/[email protected]/range.ts";
```

---

#### `numberRange`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/range.ts#L17)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/range.test.ts)_

> Constructs a numeric between `start` and `end` inclusively.

#### Import

```ts
/* Node: */ import { numberRange } from "froebel/range";
/* Deno: */ import { numberRange } from "https://deno.land/x/[email protected]/range.ts";
```

#### Example
```ts
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`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/range.ts#L43)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/range.test.ts)_

> Constructs a range between characters.

#### Import

```ts
/* Node: */ import { alphaRange } from "froebel/range";
/* Deno: */ import { alphaRange } from "https://deno.land/x/[email protected]/range.ts";
```

#### Example
```ts
range('a', 'd') // -> ['a', 'b', 'c', 'd']
range('Z', 'W') // -> ['Z', 'Y', 'X', 'W']
```
## Iterable

#### `repeat`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/repeat.ts#L11)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/repeat.test.ts)_

> Returns a generator that repeats `sequence`.

#### Import

```ts
/* Node: */ import repeat from "froebel/repeat";
/* Deno: */ import repeat from "https://deno.land/x/[email protected]/repeat.ts";
```

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

---

#### `take`

```hs
(n: number, list: Iterable) => Generator
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/take.ts#L25)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/take.test.ts)_

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

#### Import

```ts
/* Node: */ import { take } from "froebel/iterable";
/* Deno: */ import { take } from "https://deno.land/x/[email protected]/iterable.ts";
```

#### Example
```ts
[...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`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/pick.ts#L9)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/pick.test.ts)_

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

#### Import

```ts
/* Node: */ import pick from "froebel/pick";
/* Deno: */ import pick from "https://deno.land/x/[email protected]/pick.ts";
```

#### Example
```ts
pick({ a: 1, b: 2, c: 3 }, 'a', 'c') // { a: 1, c: 3 }
```

---

#### `omit`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/omit.ts#L9)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/omit.test.ts)_

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

#### Import

```ts
/* Node: */ import omit from "froebel/omit";
/* Deno: */ import omit from "https://deno.land/x/[email protected]/omit.ts";
```

#### Example
```ts
omit({ a: 1, b: 2, c: 3 }, 'a', 'c') // { b: 2 }
```

---

#### `map`

```hs
(data: Map, callback: (key: IK, value: IV) => [OK, OV]) => Map
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/map.ts#L34)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/map.test.ts)_

> Map over `data`. `data` can be a regular object, a `Map`, a `Set`, or an
> array.

#### Import

```ts
/* Node: */ import map from "froebel/map";
/* Deno: */ import map from "https://deno.land/x/[email protected]/map.ts";
```

#### Examples
```ts
// -> { a: 1, b: 2 }
map({ a: '1', b: '2' }, (key, value) => [key, parseInt(value)])
```

```ts
// -> Map([ [2, 1], [4, 3] ])
map(new Map([ [1, 2], [3, 4] ]), (key, value) => [key + 1, value - 1])
```
## Path

#### `select`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/select.ts#L16)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/select.test.ts)_

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

#### Import

```ts
/* Node: */ import select from "froebel/select";
/* Deno: */ import select from "https://deno.land/x/[email protected]/select.ts";
```

#### Example
```ts
// -> 'something'
select(
{ a: { deeply: [{ nested: { object: 'something' } }] } },
'a', 'deeply', 0, 'nested', 'object'
)
```
## Equality

#### `oneOf`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/oneOf.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/oneOf.test.ts)_

> Checks if `v` is one of `cmps`.

#### Import

```ts
/* Node: */ import oneOf from "froebel/oneOf";
/* Deno: */ import oneOf from "https://deno.land/x/[email protected]/oneOf.ts";
```

---

#### `equal`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/equal.ts#L9)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/equal.test.ts)_

> 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

```ts
/* Node: */ import equal from "froebel/equal";
/* Deno: */ import equal from "https://deno.land/x/[email protected]/equal.ts";
```

---

#### `clone`

```hs
(value: T) => T
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/clone.ts#L15)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/clone.test.ts)_

> 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

```ts
/* Node: */ import clone from "froebel/clone";
/* Deno: */ import clone from "https://deno.land/x/[email protected]/clone.ts";
```

---

#### `merge`

```hs
(a: A, b: B) => Merge
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/merge.ts#L76)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/merge.test.ts)_

> Recursively merges `A` and `B`. If a property in `A` and `B` is of a
> different type (i.e. it's not an array, Set, Map, or plain object in both,
> the value from `B` will be used in the result).
>
> If there are self-references in the cloned values, array / Set items, or Map
> keys or values, they will also be self-referencing in the result.
>

#### Import

```ts
/* Node: */ import merge from "froebel/merge";
/* Deno: */ import merge from "https://deno.land/x/
[email protected]/merge.ts";
```

## Promise

#### `promisify`

```hs
(withCallback: T, resultIndex?: N, errorIndex: null | number) => Promisified
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/promisify.ts#L56)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/promisify.test.ts)_

> Turns a function accepting a callback into a function returning a promise.
> You can specify in which parameter (if any) the callback expects to receive
> a result and in which it expects an error.
> Pass `null` to `resultIndex` or `errorIndex` if no result or errors are
> passed to the callback. By default the first argument passed to the callback
> is interpreted as result and none of the arguments as error (if the function
> accepting the callback throws or rejects, that will still result in the
> promisified function rejecting).
>
> The `callbackFirst` property allows passing additional parameters after the
> callback and `callbackLast` will pass additional parameters before the
> callback.
>

#### Import

```ts
/* Node: */ import promisify from "froebel/promisify";
/* Deno: */ import promisify from "https://deno.land/x/[email protected]/promisify.ts";
```

#### Examples
```ts
const notify = (cb: (msg: string) => void) => { msg('something') }
const waitForMessage = promisify(notify)
await waitForMessage() // -> 'something'

// here result is passed at index 1 and errors at index 0.
const callbackAPI = (cb: (error?: Error, data?: unknown) => void) => {}
const asyncAPI = promisify(callbackAPI, 1, 0)
```

```ts
const sleep = promisify(setTimeout).callbackFirst
await sleep(200)
```

```ts
const fs = require('node:fs');
const stat = promisify(fs.stat, 1, 0).callbackLast

try {
const stats = await stat('.');
console.log(`This directory is owned by ${stats.uid}`);
} catch (err) {
console.error(err)
}
```

---

#### `createQueue`

```hs
() => Queue
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/queue.ts#L46)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/queue.test.ts)_

> Creates a `queue` function that accepts a function as it's only parameter.
> When `queue` is invoked, the passed in function is executed after the last
> function passed to `queue` has finished executing. The `queue` function
> returns the result of the passed in function asynchronously.
>
> Reading `queue.done` is `true` if no functions are currently executing /
> scheduled and otherwise a promise that resolves once the last function has
> stopped executing and no futher functions are queued.
>

#### Import

```ts
/* Node: */ import createQueue from "froebel/queue";
/* Deno: */ import createQueue from "https://deno.land/x/[email protected]/queue.ts";
```

#### Example
```ts
const queue = createQueue()

queue(async () => {
console.log('start a')
await delay()
return 'end a'
}).then(console.log)

queue(async () => {
console.log('start b')
await delay()
return 'end b'
}).then(console.log)

queue(async () => {
console.log('start c')
await delay()
return 'end c'
}).then(console.log)

await queue.done

// start a
// end a
// start b
// end b
// start c
// end c
```

---

#### `isPromise`

```hs
(value: unknown) => value is Promise
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/isPromise.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/isPromise.test.ts)_

> Checks if `value` looks like a promise.

#### Import

```ts
/* Node: */ import isPromise from "froebel/isPromise";
/* Deno: */ import isPromise from "https://deno.land/x/[email protected]/isPromise.ts";
```

---

#### `isNotPromise`

```hs
(value: T) => value is Exclude>
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/isPromise.ts#L19)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/isPromise.test.ts)_

> Checks if `value` is not a promise.

#### Import

```ts
/* Node: */ import { isNotPromise } from "froebel/isPromise";
/* Deno: */ import { isNotPromise } from "https://deno.land/x/[email protected]/isPromise.ts";
```

#### Example
```ts
(value: number | Promise) => {
if (isNotPromise(value)) return value / 2
}
```
## Predicate

#### `truthy`

```hs
(value: T) => value is PickTruthy
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/truthy.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/truthy.test.ts)_

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

#### Import

```ts
/* Node: */ import { truthy } from "froebel/truthy";
/* Deno: */ import { truthy } from "https://deno.land/x/[email protected]/truthy.ts";
```

---

#### `falsy`

```hs
(value: T) => value is PickFalsy
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/truthy.ts#L5)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/truthy.test.ts)_

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

#### Import

```ts
/* Node: */ import { falsy } from "froebel/truthy";
/* Deno: */ import { falsy } from "https://deno.land/x/[email protected]/truthy.ts";
```

---

#### `nullish`

```hs
(value: T) => value is Nullish
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/nullish.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/nullish.test.ts)_

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

#### Import

```ts
/* Node: */ import { nullish } from "froebel/nullish";
/* Deno: */ import { nullish } from "https://deno.land/x/[email protected]/nullish.ts";
```

---

#### `notNullish`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/nullish.ts#L20)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/nullish.test.ts)_

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

#### Import

```ts
/* Node: */ import { notNullish } from "froebel/nullish";
/* Deno: */ import { notNullish } from "https://deno.land/x/[email protected]/nullish.ts";
```

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

---

#### `isFulfilled`

```hs
(result: PromiseSettledResult) => result is PromiseFulfilledResult
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/settled.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/settled.test.ts)_

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

#### Import

```ts
/* Node: */ import { isFulfilled } from "froebel/settled";
/* Deno: */ import { isFulfilled } from "https://deno.land/x/[email protected]/settled.ts";
```

---

#### `isRejected`

```hs
(result: PromiseSettledResult) => result is PromiseRejectedResult
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/settled.ts#L7)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/settled.test.ts)_

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

#### Import

```ts
/* Node: */ import { isRejected } from "froebel/settled";
/* Deno: */ import { isRejected } from "https://deno.land/x/[email protected]/settled.ts";
```

## String

#### `prefix`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/prefix.ts#L12)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/prefix.test.ts)_

> 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

```ts
/* Node: */ import prefix from "froebel/prefix";
/* Deno: */ import prefix from "https://deno.land/x/[email protected]/prefix.ts";
```

---

#### `suffix`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/suffix.ts#L8)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/suffix.test.ts)_

> Returns `str` suffixed with `suffix`. Same case and type behavior as
> [prefix](#prefix).

#### Import

```ts
/* Node: */ import suffix from "froebel/suffix";
/* Deno: */ import suffix from "https://deno.land/x/[email protected]/suffix.ts";
```

---

#### `surround`

```hs
(str: A, surrounding: B) => B extends "" ? A : Surround
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/surround.ts#L13)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/surround.test.ts)_

> Surrounds the `str` with `surrounding`. `surrounding` must have an even length.

#### Import

```ts
/* Node: */ import { surround } from "froebel/surround";
/* Deno: */ import { surround } from "https://deno.land/x/
[email protected]/surround.ts";
```

#### Example
```ts
surround("foo", "()") // "(foo)"
surround("foo", "({[]})") // "({[foo]})"
```

---

#### `capitalize`

```hs
(str: T) => Capitalize
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L12)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_

> Upper-case first letter of string.

#### Import

```ts
/* Node: */ import { capitalize } from "froebel/case";
/* Deno: */ import { capitalize } from "https://deno.land/x/[email protected]/case.ts";
```

---

#### `uncapitalize`

```hs
(str: T) => Uncapitalize
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L16)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_

> Lower-case first letter of string

#### Import

```ts
/* Node: */ import { uncapitalize } from "froebel/case";
/* Deno: */ import { uncapitalize } from "https://deno.land/x/[email protected]/case.ts";
```

---

#### `upper`

```hs
(str: T) => Uppercase
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L20)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_

> Strictly typed `String.toUpperCase()`.

#### Import

```ts
/* Node: */ import { upper } from "froebel/case";
/* Deno: */ import { upper } from "https://deno.land/x/[email protected]/case.ts";
```

---

#### `lower`

```hs
(str: T) => Lowercase
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L24)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_

> Strictly typed `String.toLowerCase()`.

#### Import

```ts
/* Node: */ import { lower } from "froebel/case";
/* Deno: */ import { lower } from "https://deno.land/x/[email protected]/case.ts";
```

---

#### `snake`

```hs
(str: T) => DelimitedCase
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L40)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_

> 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

```ts
/* Node: */ import { snake } from "froebel/case";
/* Deno: */ import { snake } from "https://deno.land/x/[email protected]/case.ts";
```

#### Example
```ts
snake('fooBar') // 'foo_bar'
```

---

#### `kebab`

```hs
(str: T) => DelimitedCase
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L64)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_

> Transforms a variable name to kebab case.
>
> Note: The rules for transforming anything to kebab 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

```ts
/* Node: */ import { kebab } from "froebel/case";
/* Deno: */ import { kebab } from "https://deno.land/x/[email protected]/case.ts";
```

#### Example
```ts
kebab('fooBar') // 'foo-bar'
```

---

#### `camel`

```hs
(str: T) => CamelCase
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L88)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_

> 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

```ts
/* Node: */ import { camel } from "froebel/case";
/* Deno: */ import { camel } from "https://deno.land/x/[email protected]/case.ts";
```

#### Example
```ts
camel('foo_bar') // 'fooBar'
```

---

#### `pascal`

```hs
(str: T) => Capitalize
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L109)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_

> Transforms a variable name to pascal case.
>
> Note: The rules for transforming anything to pascal 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

```ts
/* Node: */ import { pascal } from "froebel/case";
/* Deno: */ import { pascal } from "https://deno.land/x/[email protected]/case.ts";
```

#### Example
```ts
pascal('foo_bar') // 'FooBar'
```

---

#### `screamingSnake`

```hs
(str: T) => Uppercase
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L122)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_

> Transforms a variable name to screaming snake case.
>
> see [snake](#snake)

#### Import

```ts
/* Node: */ import { screamingSnake } from "froebel/case";
/* Deno: */ import { screamingSnake } from "https://deno.land/x/[email protected]/case.ts";
```

#### Example
```ts
screamingSnake('fooBar') // 'FOO_BAR'
```

---

#### `transformCase`

```hs
(str: T, targetCase: C) => DelimitedCase
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L135)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_

> Transform a variable name to `targetCase`
>
> see [snake](#snake), [kebab](#kebab), [camel](#camel), [pascal](#pascal), and [screamingSnake](#screamingsnake)

#### Import

```ts
/* Node: */ import { transformCase } from "froebel/case";
/* Deno: */ import { transformCase } from "https://deno.land/x/[email protected]/case.ts";
```

## Math

#### `clamp`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/clamp.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/clamp.test.ts)_

> Clamp `num` between `min` and `max` inclusively.

#### Import

```ts
/* Node: */ import clamp from "froebel/clamp";
/* Deno: */ import clamp from "https://deno.land/x/[email protected]/clamp.ts";
```

## Data Structures

#### `BiMap`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/bimap.ts#L172)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/bimap.test.ts)_

> 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

```ts
/* Node: */ import BiMap from "froebel/bimap";
/* Deno: */ import BiMap from "https://deno.land/x/[email protected]/bimap.ts";
```

#### Examples
```ts
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
```

```ts
// 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')()
BiMap.alias('en', 'de')([['hello', 'hallo']])
BiMap.alias('en', 'de')(new Map())
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(), new Set())
```

---

#### `SortedArray`

```hs
class SortedArray(compare: Cmp, ...value: T[])
```

_[source](https://github.com/MathisBullinger/froebel/blob/main/sortedArray.ts#L134)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/sortedArray.test.ts)_

> 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

```ts
/* Node: */ import SortedArray from "froebel/sortedArray";
/* Deno: */ import SortedArray from "https://deno.land/x/[email protected]/sortedArray.ts";
```

---

#### `SortedMap`

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

_[source](https://github.com/MathisBullinger/froebel/blob/main/sortedMap.ts#L11)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/sortedMap.test.ts)_

> 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

```ts
/* Node: */ import SortedMap from "froebel/sortedMap";
/* Deno: */ import SortedMap from "https://deno.land/x/[email protected]/sortedMap.ts";
```