{"id":14984965,"url":"https://github.com/mathisbullinger/froebel","last_synced_at":"2025-12-11T21:17:25.325Z","repository":{"id":38280878,"uuid":"393209914","full_name":"MathisBullinger/froebel","owner":"MathisBullinger","description":"A strictly typed utility library.","archived":false,"fork":false,"pushed_at":"2024-03-19T13:26:33.000Z","size":475,"stargazers_count":1113,"open_issues_count":8,"forks_count":28,"subscribers_count":13,"default_branch":"main","last_synced_at":"2025-05-15T09:08:02.872Z","etag":null,"topics":["deno","functional-programming","javascript","typescript","utility-library"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/froebel","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MathisBullinger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-08-06T01:10:50.000Z","updated_at":"2025-03-30T17:36:57.000Z","dependencies_parsed_at":"2023-12-22T13:04:14.487Z","dependency_job_id":"9a4d3683-c8dc-4edb-9e71-1074b50d2067","html_url":"https://github.com/MathisBullinger/froebel","commit_stats":{"total_commits":138,"total_committers":5,"mean_commits":27.6,"dds":0.02898550724637683,"last_synced_commit":"4860d718c8806a0bba026a2b955c2fb5b478359e"},"previous_names":["mathisbullinger/facula","mathisbullinger/snatchblock"],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MathisBullinger%2Ffroebel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MathisBullinger%2Ffroebel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MathisBullinger%2Ffroebel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MathisBullinger%2Ffroebel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MathisBullinger","download_url":"https://codeload.github.com/MathisBullinger/froebel/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254310520,"owners_count":22049470,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deno","functional-programming","javascript","typescript","utility-library"],"created_at":"2024-09-24T14:09:57.508Z","updated_at":"2025-12-11T21:17:20.283Z","avatar_url":"https://github.com/MathisBullinger.png","language":"TypeScript","readme":"# Froebel - a strictly typed TypeScript utility library.\n\nThis is my (WIP) personal collection of TypeScript helper functions and utilities that\nI use across different projects. \nThink an opinionated version of lodash, but with first-class types.\n\nIf you have an idea for a utility that might make a good addition to this collection,\nplease open an issue and suggest its inclusion.\n\nRuns in Deno, Node.js, and the Browser. Get it from [deno.land](https://deno.land/x/froebel@v0.23.2) \nor [npm](https://www.npmjs.com/package/froebel).\n\n## Installation\n\n### Using npm\n\n```shell\nnpm install froebel\n```\n\nand — assuming a module-compatible system like webpack — import as:\n\n```ts\nimport { someUtility } from 'froebel';\n// you can also import the utility you need directly:\nimport memoize from 'froebel/memoize';\n```\n\n### Using Deno\n\n```ts\nimport { someUtility } from \"https://deno.land/x/froebel@v0.23.2/mod.ts\";\n// or import just the utility you need:\nimport memoize from \"https://deno.land/x/froebel@v0.23.2/memoize.ts\"\n```\n\n---\n\n## Available Utilities\n\nEach category also has a file exporting only the utilities in that category, so\nif you want to only import utilities from one category, you could import them as\n\n```ts\nimport { throttle, debounce } from \"froebel/function\";\n```\n\nA few utils are exported from multiple categories but will only be listed here\nonce. For example `isPromise` is exported from both the `promise` and the \n`predicate` category.\n\n### Table of Contents\n\n\n\n- __`function`__\n    - [ident](#ident)\n    - [noop](#noop)\n    - [partial](#partial)\n    - [forward](#forward)\n    - [unary](#unary)\n    - [callAll](#callall)\n    - [pipe](#pipe)\n    - [applyPipe](#applypipe)\n    - [bundle](#bundle)\n    - [bundleSync](#bundlesync)\n    - [nullishChain](#nullishchain)\n    - [asyncNullishChain](#asyncnullishchain)\n    - [throttle](#throttle)\n    - [debounce](#debounce)\n    - [memoize](#memoize)\n    - [limitInvocations](#limitinvocations)\n    - [once](#once)\n- __`list`__\n    - [atWrap](#atwrap)\n    - [zip](#zip)\n    - [zipWith](#zipwith)\n    - [unzip](#unzip)\n    - [unzipWith](#unzipwith)\n    - [batch](#batch)\n    - [partition](#partition)\n    - [shuffle](#shuffle)\n    - [shuffleInPlace](#shuffleinplace)\n    - [take](#take)\n    - [range](#range)\n    - [numberRange](#numberrange)\n    - [alphaRange](#alpharange)\n- __`iterable`__\n    - [repeat](#repeat)\n    - [take](#take)\n- __`object`__\n    - [pick](#pick)\n    - [omit](#omit)\n    - [map](#map)\n- __`path`__\n    - [select](#select)\n- __`equality`__\n    - [oneOf](#oneof)\n    - [equal](#equal)\n    - [clone](#clone)\n    - [merge](#merge)\n- __`promise`__\n    - [promisify](#promisify)\n    - [createQueue](#createqueue)\n    - [isPromise](#ispromise)\n    - [isNotPromise](#isnotpromise)\n- __`predicate`__\n    - [truthy](#truthy)\n    - [falsy](#falsy)\n    - [nullish](#nullish)\n    - [notNullish](#notnullish)\n    - [isFulfilled](#isfulfilled)\n    - [isRejected](#isrejected)\n- __`string`__\n    - [prefix](#prefix)\n    - [suffix](#suffix)\n    - [surround](#surround)\n    - [capitalize](#capitalize)\n    - [uncapitalize](#uncapitalize)\n    - [upper](#upper)\n    - [lower](#lower)\n    - [snake](#snake)\n    - [kebab](#kebab)\n    - [camel](#camel)\n    - [pascal](#pascal)\n    - [screamingSnake](#screamingsnake)\n    - [transformCase](#transformcase)\n- __`math`__\n    - [clamp](#clamp)\n- __`data structures`__\n    - [BiMap](#bimap)\n    - [SortedArray](#sortedarray)\n    - [SortedMap](#sortedmap)\n\n\n\n## Function\n\n#### `ident` \n  \n```hs\n(value: T) =\u003e T\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/ident.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/ident.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Identity function.\n\n\n#### Import\n\n```ts\n/* Node: */  import ident from \"froebel/ident\";\n/* Deno: */  import ident from \"https://deno.land/x/froebel@v0.23.2/ident.ts\";\n```\n\n\n\n\n---\n\n#### `noop` \n  \n```hs\n() =\u003e void\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/noop.ts#L1)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/noop.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\n\n\n#### Import\n\n```ts\n/* Node: */  import noop from \"froebel/noop\";\n/* Deno: */  import noop from \"https://deno.land/x/froebel@v0.23.2/noop.ts\";\n```\n\n\n\n\n---\n\n#### `partial` \n  \n```hs\n(fun: T, ...argsLeft: PL) =\u003e (...argsRight: PR) =\u003e ReturnType\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/partial.ts#L17)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/partial.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Partially apply a function.\n\n\n#### Import\n\n```ts\n/* Node: */  import partial from \"froebel/partial\";\n/* Deno: */  import partial from \"https://deno.land/x/froebel@v0.23.2/partial.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst divide = (dividend: number, divisor: number) =\u003e dividend / divisor\n\n// (divisor: number) =\u003e number\nconst oneOver = partial(divide, 1)\n\n// prints: 0.25\nconsole.log(oneOver(4))\n```\n\n---\n\n#### `forward` \n  \n```hs\n(fun: T, ...argsRight: PR) =\u003e (...argsLeft: PL) =\u003e ReturnType\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/forward.ts#L28)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/forward.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Given a function and its nth..last arguments, return a function accepting\n\u003e arguments 0..n-1.\n\n\n#### Import\n\n```ts\n/* Node: */  import forward from \"froebel/forward\";\n/* Deno: */  import forward from \"https://deno.land/x/froebel@v0.23.2/forward.ts\";\n```\n\n\n\n\n#### Examples\n```ts\nconst divide = (dividend: number, divisor: number) =\u003e dividend / divisor\n\n// (dividend: number) =\u003e number\nconst divideBy2 = forward(divide, 2)\n\n// prints: 0.5\nconsole.log(divideBy2(1))\n```\n\n```ts\nconst fetchUrl = async (protocol: string, domain: string, path: string) =\u003e\n  await fetch(`${protocol}://${domain}/${path}`)\n\nconst fetchRepo = forward(fetchUrl, 'github.com', 'MathisBullinger/froebel')\n\nconst viaHTTPS = await fetchRepo('https')\n```\n\n---\n\n#### `unary` \n  \n```hs\n(fun: T) =\u003e Unary\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/unary.ts#L15)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/unary.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Turns `fun` into a unary function (a function that only accepts one\n\u003e argument).\n\u003e \n\u003e Note: `fun` must accept at least one argument and must not require more than\n\u003e one argument.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import unary from \"froebel/unary\";\n/* Deno: */  import unary from \"https://deno.land/x/froebel@v0.23.2/unary.ts\";\n```\n\n\n\n\n#### Example\n```ts\n['1', '2', '3'].map(unary(parseInt))  // -\u003e [1, 2, 3]\n```\n\n---\n\n#### `callAll` \n  \n```hs\n(funs: F[], ...args: P) =\u003e ReturnTypes\u003cF\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/callAll.ts#L16)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/callAll.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Take a list of functions that accept the same parameters and call them all\n\u003e with the provided arguments.\n\n\n#### Import\n\n```ts\n/* Node: */  import callAll from \"froebel/callAll\";\n/* Deno: */  import callAll from \"https://deno.land/x/froebel@v0.23.2/callAll.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst mult = (a: number, b: number) =\u003e a * b\nconst div  = (a: number, b: number) =\u003e a / b\n\n// prints: [8, 2]\nconsole.log( callAll([mult, div], 4, 2) )\n```\n\n---\n\n#### `pipe` \n  \n```hs\n(...funs: T) =\u003e PipedFun\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/pipe.ts#L27)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/pipe.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Given a list of functions returns a function that will execute the given\n\u003e functions one after another, always passing the result of the previous\n\u003e function as an argument to the next function.\n\u003e \n\u003e If one of the given functions returns a promise, the promise will be resolved\n\u003e before being passed to the next function.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import pipe from \"froebel/pipe\";\n/* Deno: */  import pipe from \"https://deno.land/x/froebel@v0.23.2/pipe.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst join = (...chars: string[]) =\u003e chars.join('')\npipe(join, parseInt)('1', '2', '3')  // -\u003e 123\n\nconst square = (n: number) =\u003e n ** 2\n\n// this is equivalent to: square(square(square(2)))\npipe(square, square, square)(2)  // -\u003e 256\n\n// also works with promises:\nfetchNumber :: async () =\u003e Promise\u003cnumber\u003e\npipe(fetchNumber, n =\u003e n.toString())  // async () =\u003e Promise\u003cstring\u003e\n```\n\n---\n\n#### `applyPipe` \n  \n```hs\n(arg: Parameters\u003cT[0]\u003e[0], ...funs: T) =\u003e CheckPipe\u003cT, CarryReturn\u003cReturnTypes\u003cT\u003e, Parameters\u003cT[0]\u003e\u003e, false\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/pipe.ts#L57)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/pipe.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Like `pipe` but takes an argument as its first parameter and invokes the pipe\n\u003e with it.\n\u003e \n\u003e Note: unlike in `pipe`, the first function of the pipe must take exactly one\n\u003e argument.\n\u003e \n\u003e \n\u003e \u003csub\u003esee [pipe](#pipe)\u003c/sub\u003e\n\n\n#### Import\n\n```ts\n/* Node: */  import { applyPipe } from \"froebel/pipe\";\n/* Deno: */  import { applyPipe } from \"https://deno.land/x/froebel@v0.23.2/pipe.ts\";\n```\n\n\n\n\n#### Example\n```ts\napplyPipe(2, double, square, half)  // -\u003e 8\n```\n\n---\n\n#### `bundle` \n  \n```hs\n(...funs: λ\u003cT\u003e[]) =\u003e (...args: T) =\u003e Promise\u003cvoid\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/bundle.ts#L12)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/bundle.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Given a list of functions that accept the same parameters, returns a function\n\u003e that takes these parameters and invokes all of the given functions.\n\u003e \n\u003e The returned function returns a promise that resolves once all functions\n\u003e returned/resolved and rejects if any of the functions throws/rejects - but\n\u003e only after all returned promises have been settled.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import bundle from \"froebel/bundle\";\n/* Deno: */  import bundle from \"https://deno.land/x/froebel@v0.23.2/bundle.ts\";\n```\n\n\n\n\n---\n\n#### `bundleSync` \n  \n```hs\n(...funs: λ\u003cT\u003e[]) =\u003e (...args: T) =\u003e void\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/bundle.ts#L29)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/bundle.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Same as [bundle](#bundle), but return synchronously.\n\u003e \n\u003e If any of the functions throws an error synchronously, none of the functions\n\u003e after it will be invoked and the error will propagate.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import { bundleSync } from \"froebel/bundle\";\n/* Deno: */  import { bundleSync } from \"https://deno.land/x/froebel@v0.23.2/bundle.ts\";\n```\n\n\n\n\n---\n\n#### `nullishChain` \n  \n```hs\n(...funs: [] | [FF, ...FR[]]) =\u003e (...args: Parameters\u003cFF\u003e) =\u003e ReturnType\u003cFF\u003e | ReturnType\u003cFR[number]\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/nullishChain.ts#L26)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/nullishChain.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Given a list of functions that accept the same parameters, returns a function\n\u003e that given these arguments returns the result of the first function whose\n\u003e result is not nullish.\n\u003e \n\u003e This is equivalent to chaining together invocations of the passed in\n\u003e functions with the given arguments with nullish coalescing _(`??`)_ operators.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import { nullishChain } from \"froebel/nullishChain\";\n/* Deno: */  import { nullishChain } from \"https://deno.land/x/froebel@v0.23.2/nullishChain.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst isAdult   = (age: number) =\u003e { if (n \u003e= 18) return 'adult' }\nconst isToddler = (age: number) =\u003e { if (n \u003c= 3) return 'toddler' }\n\nconst ageGroup = nullishChain(isAdult, isToddler, () =\u003e 'child')\n\n// this is functionally equivalent to:\nconst ageGroup = age =\u003e isAdult(age) ?? isToddler(age) ?? 'child'\n\nageGroup(1)  // prints: 'toddler'\nageGroup(10) // prints: 'child'\nageGroup(50) // prints: 'adult'\n```\n\n---\n\n#### `asyncNullishChain` \n  \n```hs\n(...funs: [] | [FF, ...FR[]]) =\u003e (...args: Parameters\u003cFF\u003e) =\u003e Promise\u003cPromType\u003cReturnType\u003cFF\u003e\u003e | PromType\u003cReturnType\u003cFR[number]\u003e\u003e\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/nullishChain.ts#L45)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/nullishChain.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Same as [nullishChain](#nullishchain) but accept asynchronous functions too.\n\n\n#### Import\n\n```ts\n/* Node: */  import { asyncNullishChain } from \"froebel/nullishChain\";\n/* Deno: */  import { asyncNullishChain } from \"https://deno.land/x/froebel@v0.23.2/nullishChain.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst readFromCache = (id: string): Resource =\u003e { if (id in cache) return cache[id] }\nconst readFromFile  = (id: string): Resource =\u003e { if (fileExists(id)) return readFile(id) }\nconst fetchFromNet  = async (id: string): Promise\u003cResource\u003e =\u003e await fetch(`someURL/${id}`)\n\n// async (id: string) =\u003e Promise\u003cResource\u003e\nconst getResource = asyncNullishChain(readFromCache, readFromFile, fetchFromNet)\n```\n\n---\n\n#### `throttle` \n  \n```hs\n(fun: T, ms: number, opts?: {leading: boolean, trailing: boolean}) =\u003e λ\u003cParameters\u003cT\u003e, void\u003e \u0026 {[cancel]: () =\u003e void}\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/throttle.ts#L14)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/throttle.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Create a throttled function that invokes `fun` at most every `ms` milliseconds.\n\u003e \n\u003e `fun` is invoked with the last arguments passed to the throttled function.\n\u003e \n\u003e Calling `[throttle.cancel]()` on the throttled function will cancel the currently\n\u003e scheduled invocation.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import throttle from \"froebel/throttle\";\n/* Deno: */  import throttle from \"https://deno.land/x/froebel@v0.23.2/throttle.ts\";\n```\n\n\n\n\n---\n\n#### `debounce` \n  \n```hs\n(fun: T, ms: number) =\u003e λ\u003cParameters\u003cT\u003e, void\u003e \u0026 {[cancel]: () =\u003e void}\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/debounce.ts#L14)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/debounce.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Creates a debounced function that delays invoking `fun` until `ms` milliseconds\n\u003e have passed since the last invocation of the debounced function.\n\u003e \n\u003e `fun` is invoked with the last arguments passed to the debounced function.\n\u003e \n\u003e Calling `[debounce.cancel]()` on the debounced function will cancel the currently\n\u003e scheduled invocation.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import debounce from \"froebel/debounce\";\n/* Deno: */  import debounce from \"https://deno.land/x/froebel@v0.23.2/debounce.ts\";\n```\n\n\n\n\n---\n\n#### `memoize` \n  \n```hs\n(fun: T, opt: {limit: number, weak: W, key: (...args: Parameters\u003cT\u003e) =\u003e K}) =\u003e T \u0026 {cache: W extends false ? Map\u003cK, ReturnType\u003cT\u003e\u003e : Cache\u003cK, ReturnType\u003cT\u003e\u003e}\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/memoize.ts#L70)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/memoize.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Returns a copy of `fun` that remembers its result for any given arguments and\n\u003e only invokes `fun` for unknown arguments.\n\u003e \n\u003e The cache key is computed using the `key` function. The default `key`\n\u003e function simply stringifies the arguments.\n\u003e \n\u003e If `limit` is specified, only the `limit`-last entries are kept in cache.\n\u003e \n\u003e The function's cache is available at `memoized.cache`.\n\u003e \n\u003e If `opt.weak` is `true`, non-primitive cache keys are stored in a WeakMap.\n\u003e This behavior might for example be useful if you want to memoize some\n\u003e calculation including a DOM Node without holding on to a reference of that\n\u003e node.\n\u003e Using weak keys prohibits setting a `limit`.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import memoize from \"froebel/memoize\";\n/* Deno: */  import memoize from \"https://deno.land/x/froebel@v0.23.2/memoize.ts\";\n```\n\n\n\n\n#### Examples\n```ts\nconst expensiveCalculation = (a: number, b: number) =\u003e {\n  console.log(`calculate ${a} + ${b}`)\n  return a + b\n}\nconst calc = memoize(expensiveCalculation)\n\nconsole.log( calc(1, 2) )\n// calculate 1 + 2\n// 3\nconsole.log( calc(20, 5) )\n// calculate 20 + 5\n// 25\nconsole.log( calc(20, 5) )\n// 25\nconsole.log( calc(1, 2) )\n// 3\n\ncalc.cache.clear()\nconsole.log( calc(1, 2) )\n// calculate 1 + 2\n// 3\n```\n\n```ts\nconst logIfDifferent = memoize(\n  (msg: string) =\u003e console.log(msg),\n  {\n    limit: 1,\n    key: msg =\u003e msg\n  }\n)\n\nlogIfDifferent('a')\nlogIfDifferent('a')\nlogIfDifferent('b')\nlogIfDifferent('a')\n\n// a\n// b\n// a\n```\n\n---\n\n#### `limitInvocations` \n  \n```hs\n(fun: T, limit: number, ...funs: ExcS\u003cT\u003e) =\u003e T\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/invoke.ts#L19)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/invoke.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Returns a version of the function `fun` that can only be invoked `limit`\n\u003e times.\n\u003e An optional `except` function will be called with the same parameters on any\n\u003e additional invocations.\n\u003e \n\u003e If `fun` returns anything but `void` (or `Promise\u003cvoid\u003e`), supplying an\n\u003e `except` function is mandatory.\n\u003e \n\u003e The `except` function must have the same return type as `fun`, or — if `fun`\n\u003e returns a promise — it may return the type that the promise resolves to\n\u003e synchronously.\n\u003e \n\u003e The `except` function may also throw instead of returning a value.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import { limitInvocations } from \"froebel/invoke\";\n/* Deno: */  import { limitInvocations } from \"https://deno.land/x/froebel@v0.23.2/invoke.ts\";\n```\n\n\n\n\n---\n\n#### `once` \n  \n```hs\n(fun: T, ...funs: ExcS\u003cT\u003e) =\u003e T\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/invoke.ts#L40)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/invoke.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Special case of [limitInvocations](#limitinvocations). `fun` can only be invoked once.\n\u003e \n\u003e \u003csub\u003esee [limitInvocations](#limitinvocations)\u003c/sub\u003e\n\n\n#### Import\n\n```ts\n/* Node: */  import { once } from \"froebel/invoke\";\n/* Deno: */  import { once } from \"https://deno.land/x/froebel@v0.23.2/invoke.ts\";\n```\n\n\n\n## List\n\n#### `atWrap` \n  \n```hs\n(arr: T[], i: number) =\u003e T\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/atWrap.ts#L3)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/atWrap.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Access list at `i % length`. Negative indexes start indexing the last\n\u003e element as `[-1]` and wrap around to the back.\n\n\n#### Import\n\n```ts\n/* Node: */  import atWrap from \"froebel/atWrap\";\n/* Deno: */  import atWrap from \"https://deno.land/x/froebel@v0.23.2/atWrap.ts\";\n```\n\n\n\n\n---\n\n#### `zip` \n  \n```hs\n(...lists: T) =\u003e Zip\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/zip.ts#L16)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/zip.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Takes multiple lists and returns a list of tuples containing the value in\n\u003e each list at the current index. If the lists are of different lengths, the\n\u003e returned list of tuples has the length of the shortest passed in list.\n\n\n#### Import\n\n```ts\n/* Node: */  import zip from \"froebel/zip\";\n/* Deno: */  import zip from \"https://deno.land/x/froebel@v0.23.2/zip.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst pairs = zip([1,2,3], ['a','b','c'])\nconsole.log(pairs) // prints: [[1,'a'], [2,'b'], [3,'c']]\n```\n\n---\n\n#### `zipWith` \n  \n```hs\n(zipper: (...args: {[I in string | number | symbol]: U}) =\u003e U, ...lists: T) =\u003e U[]\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/zip.ts#L35)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/zip.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Same as [zip](#zip) but also takes a `zipper` function, that is called for\n\u003e each index with the element at current index in each list as arguments. The\n\u003e result of `zipper` is the element at current index in the list returned from\n\u003e `zipWith`.\n\n\n#### Import\n\n```ts\n/* Node: */  import { zipWith } from \"froebel/zip\";\n/* Deno: */  import { zipWith } from \"https://deno.land/x/froebel@v0.23.2/zip.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst sums = zipWith((a,b) =\u003e a+b, [1,2,3], [4,5,6])\nconsole.log(sums) // prints: [5,7,9]\n```\n\n---\n\n#### `unzip` \n  \n```hs\n(...zipped: T[][]) =\u003e Unzip\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/unzip.ts#L15)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/unzip.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Reverse of [zip](#zip). Takes a list of tuples and deconstructs them into\n\u003e an array (of length of the tuples length) of lists each containing all the\n\u003e elements in all tuples at the lists index.\n\n\n#### Import\n\n```ts\n/* Node: */  import unzip from \"froebel/unzip\";\n/* Deno: */  import unzip from \"https://deno.land/x/froebel@v0.23.2/unzip.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst [nums, chars] = unzip([1,'a'], [2,'b'], [3,'c'])\nconsole.log(nums)  // prints: [1, 2, 3]\nconsole.log(chars) // prints: ['a','b','c']\n```\n\n---\n\n#### `unzipWith` \n  \n```hs\n(zipped: T[][], ...unzippers: U) =\u003e {[I in string | number | symbol]: ReturnType\u003cU[I]\u003e}\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/unzip.ts#L39)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/unzip.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Same as [unzip](#unzip) but accepts an `unzipper` function for each tuple\n\u003e index. The `unzipper`'s return value is used as the value in the list at\n\u003e that index returned from `unzipWith`.\n\u003e \n\u003e The `unzipper` takes the current element as its first argument, an\n\u003e accumulator as second argument (initially `undefined`) and its return value\n\u003e is the accumulator passed into the next invocation.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import { unzipWith } from \"froebel/unzip\";\n/* Deno: */  import { unzipWith } from \"https://deno.land/x/froebel@v0.23.2/unzip.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst [nums, str] = unzipWith(\n  [ [1,'a'], [2,'b'], [3,'c'] ],\n  (n, acc: number[] = []) =\u003e [...acc, n],\n  (c, str = '') =\u003e str + c\n)\n\nconsole.log(nums) // prints: [1, 2, 3]\nconsole.log(str)  // prints: 'abc'\n```\n\n---\n\n#### `batch` \n  \n```hs\n(list: T[], batchSize: number) =\u003e T[][]\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/batch.ts#L14)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/batch.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Takes a `list` and returns it in multiple smaller lists of the size\n\u003e `batchSize`.\n\u003e The last batch may be smaller than `batchSize` depending on if `list` size is\n\u003e divisible by `batchSize`.\n\n\n#### Import\n\n```ts\n/* Node: */  import batch from \"froebel/batch\";\n/* Deno: */  import batch from \"https://deno.land/x/froebel@v0.23.2/batch.ts\";\n```\n\n\n\n\n#### Example\n```ts\nbatch([1,2,3,4,5], 2)  // -\u003e [ [1,2], [3,4], [5] ]\n```\n\n---\n\n#### `partition` \n  \n```hs\n(list: T[], predicate: (el: T) =\u003e el is S) =\u003e [S[], Exclude\u003cT, S\u003e[]]\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/partition.ts#L30)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/partition.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Takes a `list` and returns a pair of lists containing: the elements that\n\u003e match the `predicate` and those that don't, respectively.\n\u003e \n\u003e Think of it as `filter`, but the elements that don't pass the filter aren't\n\u003e discarded but returned in a separate list instead.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import partition from \"froebel/partition\";\n/* Deno: */  import partition from \"https://deno.land/x/froebel@v0.23.2/partition.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst [strings, numbers] = partition(\n  ['a', 'b', 1, 'c', 2, 3],\n  (el): el is string =\u003e typeof el === 'string'\n)\n// strings: [\"a\", \"b\", \"c\"]\n// numbers: [1, 2, 3]\n```\n\n---\n\n#### `shuffle` \n  \n```hs\n(list: T[]) =\u003e T[]\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/shuffle.ts#L5)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/shuffle.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Shuffles `list` using the Fisher-Yates shuffle algorithm.\n\u003e The original `list` is not modified and the shuffled list is returned.\n\n\n#### Import\n\n```ts\n/* Node: */  import shuffle from \"froebel/shuffle\";\n/* Deno: */  import shuffle from \"https://deno.land/x/froebel@v0.23.2/shuffle.ts\";\n```\n\n\n\n\n---\n\n#### `shuffleInPlace` \n  \n```hs\n(list: unknown[]) =\u003e void\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/shuffle.ts#L16)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/shuffle.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Same as [shuffle](#shuffle) but shuffles `list` in place.\n\n\n#### Import\n\n```ts\n/* Node: */  import { shuffleInPlace } from \"froebel/shuffle\";\n/* Deno: */  import { shuffleInPlace } from \"https://deno.land/x/froebel@v0.23.2/shuffle.ts\";\n```\n\n\n\n\n---\n\n#### `take` \n  \n```hs\n(n: number, list: Iterable\u003cT\u003e) =\u003e T[]\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/take.ts#L11)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/take.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Takes `n` elements from the iterable `list` and returns them as an array.\n\n\n#### Import\n\n```ts\n/* Node: */  import { take } from \"froebel/list\";\n/* Deno: */  import { take } from \"https://deno.land/x/froebel@v0.23.2/list.ts\";\n```\n\n\n\n\n#### Example\n```ts\ntake(5, repeat(1, 2))  // -\u003e [1, 2, 1, 2, 1]\ntake(3, [1, 2, 3, 4])  // -\u003e [1, 2, 3]\ntake(3, [1, 2])        // -\u003e [1, 2]\n```\n\n---\n\n#### `range` \n  \n\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/range.ts#L66)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/range.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Creates a range between two values.\n\u003e \n\u003e \u003csub\u003esee [numberRange](#numberrange) and [alphaRange](#alpharange)\u003c/sub\u003e\n\n\n#### Import\n\n```ts\n/* Node: */  import range from \"froebel/range\";\n/* Deno: */  import range from \"https://deno.land/x/froebel@v0.23.2/range.ts\";\n```\n\n\n\n\n---\n\n#### `numberRange` \n  \n```hs\n(start: number, end: number, step: number) =\u003e number[]\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/range.ts#L17)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/range.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Constructs a numeric between `start` and `end` inclusively.\n\n\n#### Import\n\n```ts\n/* Node: */  import { numberRange } from \"froebel/range\";\n/* Deno: */  import { numberRange } from \"https://deno.land/x/froebel@v0.23.2/range.ts\";\n```\n\n\n\n\n#### Example\n```ts\nrange(2, 6)      // -\u003e [2, 3, 4, 5, 6]\nrange(8, 9, .3)  // -\u003e [8, 8.3, 8.6, 8.9]\nrange(3, -2)     // -\u003e [3, 2, 1, 0, -1, -2]\n```\n\n---\n\n#### `alphaRange` \n  \n```hs\n(start: string, end: string) =\u003e string[]\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/range.ts#L43)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/range.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Constructs a range between characters.\n\n\n#### Import\n\n```ts\n/* Node: */  import { alphaRange } from \"froebel/range\";\n/* Deno: */  import { alphaRange } from \"https://deno.land/x/froebel@v0.23.2/range.ts\";\n```\n\n\n\n\n#### Example\n```ts\nrange('a', 'd')  // -\u003e ['a', 'b', 'c', 'd']\nrange('Z', 'W')  // -\u003e ['Z', 'Y', 'X', 'W']\n```\n## Iterable\n\n#### `repeat` \n  \n```hs\n(...sequence: [T, ...T[]]) =\u003e Generator\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/repeat.ts#L11)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/repeat.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Returns a generator that repeats `sequence`.\n\n\n#### Import\n\n```ts\n/* Node: */  import repeat from \"froebel/repeat\";\n/* Deno: */  import repeat from \"https://deno.land/x/froebel@v0.23.2/repeat.ts\";\n```\n\n\n\n\n#### Example\n```ts\n// prints: 1, 2, 3, 1, 2, 3, ...\nfor (const n of repeat(1, 2, 3))\n  console.log(n)\n```\n\n---\n\n#### `take` \n  \n```hs\n(n: number, list: Iterable\u003cT\u003e) =\u003e Generator\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/take.ts#L25)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/take.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Takes `n` elements from the iterable `list` and returns them as a generator.\n\n\n#### Import\n\n```ts\n/* Node: */  import { take } from \"froebel/iterable\";\n/* Deno: */  import { take } from \"https://deno.land/x/froebel@v0.23.2/iterable.ts\";\n```\n\n\n\n\n#### Example\n```ts\n[...take(5, repeat(1, 2))]  // -\u003e [1, 2, 1, 2, 1]\n[...take(3, [1, 2, 3, 4])]  // -\u003e [1, 2, 3]\n[...take(3, [1, 2])]        // -\u003e [1, 2]\n```\n## Object\n\n#### `pick` \n  \n```hs\n(obj: T, ...keys: K[]) =\u003e Pick\u003cT, K\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/pick.ts#L9)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/pick.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e From `obj`, create a new object that only includes `keys`.\n\n\n#### Import\n\n```ts\n/* Node: */  import pick from \"froebel/pick\";\n/* Deno: */  import pick from \"https://deno.land/x/froebel@v0.23.2/pick.ts\";\n```\n\n\n\n\n#### Example\n```ts\npick({ a: 1, b: 2, c: 3 }, 'a', 'c') // { a: 1, c: 3 }\n```\n\n---\n\n#### `omit` \n  \n```hs\n(obj: T, ...keys: K[]) =\u003e Omit\u003cT, K\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/omit.ts#L9)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/omit.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e From `obj`, create a new object that does not include `keys`.\n\n\n#### Import\n\n```ts\n/* Node: */  import omit from \"froebel/omit\";\n/* Deno: */  import omit from \"https://deno.land/x/froebel@v0.23.2/omit.ts\";\n```\n\n\n\n\n#### Example\n```ts\nomit({ a: 1, b: 2, c: 3 }, 'a', 'c') // { b: 2 }\n```\n\n---\n\n#### `map` \n  \n```hs\n(data: Map\u003cIK, IV\u003e, callback: (key: IK, value: IV) =\u003e [OK, OV]) =\u003e Map\u003cOK, OV\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/map.ts#L34)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/map.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Map over `data`. `data` can be a regular object, a `Map`, a `Set`, or an\n\u003e array.\n\n\n#### Import\n\n```ts\n/* Node: */  import map from \"froebel/map\";\n/* Deno: */  import map from \"https://deno.land/x/froebel@v0.23.2/map.ts\";\n```\n\n\n\n\n#### Examples\n```ts\n// -\u003e { a: 1, b: 2 }\nmap({ a: '1', b: '2' }, (key, value) =\u003e [key, parseInt(value)])\n```\n\n```ts\n// -\u003e Map([ [2, 1], [4, 3] ])\nmap(new Map([ [1, 2], [3, 4] ]), (key, value) =\u003e [key + 1, value - 1])\n```\n## Path\n\n#### `select` \n  \n```hs\n(obj: T, ...path: P) =\u003e PickPath\u003cT, P\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/select.ts#L16)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/select.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Returns the value in `obj` at `path`. If the given path does not exist,\n\u003e the symbol `none` is returned.\n\n\n#### Import\n\n```ts\n/* Node: */  import select from \"froebel/select\";\n/* Deno: */  import select from \"https://deno.land/x/froebel@v0.23.2/select.ts\";\n```\n\n\n\n\n#### Example\n```ts\n// -\u003e 'something'\nselect(\n  { a: { deeply: [{ nested: { object: 'something' } }] } },\n  'a', 'deeply', 0, 'nested', 'object'\n)\n```\n## Equality\n\n#### `oneOf` \n  \n```hs\n(value: T, ...cmps: TT) =\u003e value is TT[number]\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/oneOf.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/oneOf.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Checks if `v` is one of `cmps`.\n\n\n#### Import\n\n```ts\n/* Node: */  import oneOf from \"froebel/oneOf\";\n/* Deno: */  import oneOf from \"https://deno.land/x/froebel@v0.23.2/oneOf.ts\";\n```\n\n\n\n\n---\n\n#### `equal` \n  \n```hs\n(a: unknown, b: unknown) =\u003e boolean\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/equal.ts#L9)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/equal.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Checks if `a` and `b` are structurally equal using the following algorithm:\n\u003e \n\u003e - primitives are compared by value\n\u003e - functions are compared by reference\n\u003e - objects (including arrays) are checked to have the same properties and\n\u003e   their values are compared recursively using the same algorithm\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import equal from \"froebel/equal\";\n/* Deno: */  import equal from \"https://deno.land/x/froebel@v0.23.2/equal.ts\";\n```\n\n\n\n\n---\n\n#### `clone` \n  \n```hs\n(value: T) =\u003e T\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/clone.ts#L15)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/clone.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Returns a copied version of `value`.\n\u003e \n\u003e If `value` is primitive, returns `value`.\n\u003e Otherwise, properties of `value` are copied recursively. Only `value`'s own\n\u003e enumerable properties are cloned. Arrays are cloned by mapping over their\n\u003e elements.\n\u003e \n\u003e If a path in `value` references itself or a parent path, then in the\n\u003e resulting object that path will also reference the path it referenced in the\n\u003e original object (but now in the resuling object instead of the original).\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import clone from \"froebel/clone\";\n/* Deno: */  import clone from \"https://deno.land/x/froebel@v0.23.2/clone.ts\";\n```\n\n\n\n\n---\n\n#### `merge` \n  \n```hs\n(a: A, b: B) =\u003e Merge\u003cA, B\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/merge.ts#L76)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/merge.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Recursively merges `A` and `B`. If a property in `A` and `B` is of a\n\u003e different type (i.e. it's not an array, Set, Map, or plain object in both,\n\u003e the value from `B` will be used in the result).\n\u003e \n\u003e If there are self-references in the cloned values, array / Set items, or Map\n\u003e keys or values, they will also be self-referencing in the result.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import merge from \"froebel/merge\";\n/* Deno: */  import merge from \"https://deno.land/x/froebel@v0.23.2/merge.ts\";\n```\n\n\n\n## Promise\n\n#### `promisify` \n  \n```hs\n(withCallback: T, resultIndex?: N, errorIndex: null | number) =\u003e Promisified\u003cT, N\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/promisify.ts#L56)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/promisify.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Turns a function accepting a callback into a function returning a promise.\n\u003e You can specify in which parameter (if any) the callback expects to receive\n\u003e a result and in which it expects an error.\n\u003e Pass `null` to `resultIndex` or `errorIndex` if no result or errors are\n\u003e passed to the callback. By default the first argument passed to the callback\n\u003e is interpreted as result and none of the arguments as error (if the function\n\u003e accepting the callback throws or rejects, that will still result in the\n\u003e promisified function rejecting).\n\u003e \n\u003e The `callbackFirst` property allows passing additional parameters after the\n\u003e callback and `callbackLast` will pass additional parameters before the\n\u003e callback.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import promisify from \"froebel/promisify\";\n/* Deno: */  import promisify from \"https://deno.land/x/froebel@v0.23.2/promisify.ts\";\n```\n\n\n\n\n#### Examples\n```ts\nconst notify = (cb: (msg: string) =\u003e void) =\u003e { msg('something') }\nconst waitForMessage = promisify(notify)\nawait waitForMessage()  // -\u003e 'something'\n\n// here result is passed at index 1 and errors at index 0.\nconst callbackAPI = (cb: (error?: Error, data?: unknown) =\u003e void) =\u003e {}\nconst asyncAPI = promisify(callbackAPI, 1, 0)\n```\n\n```ts\nconst sleep = promisify(setTimeout).callbackFirst\nawait sleep(200)\n```\n\n```ts\nconst fs = require('node:fs');\nconst stat = promisify(fs.stat, 1, 0).callbackLast\n\ntry {\n  const stats = await stat('.');\n  console.log(`This directory is owned by ${stats.uid}`);\n} catch (err) {\n  console.error(err)\n}\n```\n\n---\n\n#### `createQueue` \n  \n```hs\n() =\u003e Queue\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/queue.ts#L46)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/queue.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Creates a `queue` function that accepts a function as it's only parameter.\n\u003e When `queue` is invoked, the passed in function is executed after the last\n\u003e function passed to `queue` has finished executing. The `queue` function\n\u003e returns the result of the passed in function asynchronously.\n\u003e \n\u003e Reading `queue.done` is `true` if no functions are currently executing /\n\u003e scheduled and otherwise a promise that resolves once the last function has\n\u003e stopped executing and no futher functions are queued.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import createQueue from \"froebel/queue\";\n/* Deno: */  import createQueue from \"https://deno.land/x/froebel@v0.23.2/queue.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst queue = createQueue()\n\nqueue(async () =\u003e {\n  console.log('start a')\n  await delay()\n  return 'end a'\n}).then(console.log)\n\nqueue(async () =\u003e {\n  console.log('start b')\n  await delay()\n  return 'end b'\n}).then(console.log)\n\nqueue(async () =\u003e {\n  console.log('start c')\n  await delay()\n  return 'end c'\n}).then(console.log)\n\nawait queue.done\n\n// start a\n// end a\n// start b\n// end b\n// start c\n// end c\n```\n\n---\n\n#### `isPromise` \n  \n```hs\n(value: unknown) =\u003e value is Promise\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/isPromise.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/isPromise.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Checks if `value` looks like a promise.\n\n\n#### Import\n\n```ts\n/* Node: */  import isPromise from \"froebel/isPromise\";\n/* Deno: */  import isPromise from \"https://deno.land/x/froebel@v0.23.2/isPromise.ts\";\n```\n\n\n\n\n---\n\n#### `isNotPromise` \n  \n```hs\n(value: T) =\u003e value is Exclude\u003cT, Promise\u003cany\u003e\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/isPromise.ts#L19)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/isPromise.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Checks if `value` is not a promise.\n\n\n#### Import\n\n```ts\n/* Node: */  import { isNotPromise } from \"froebel/isPromise\";\n/* Deno: */  import { isNotPromise } from \"https://deno.land/x/froebel@v0.23.2/isPromise.ts\";\n```\n\n\n\n\n#### Example\n```ts\n(value: number | Promise\u003cunknown\u003e) =\u003e {\n  if (isNotPromise(value)) return value / 2\n}\n```\n## Predicate\n\n#### `truthy` \n  \n```hs\n(value: T) =\u003e value is PickTruthy\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/truthy.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/truthy.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Checks if `value` is truthy. Literal types are narrowed accordingly.\n\n\n#### Import\n\n```ts\n/* Node: */  import { truthy } from \"froebel/truthy\";\n/* Deno: */  import { truthy } from \"https://deno.land/x/froebel@v0.23.2/truthy.ts\";\n```\n\n\n\n\n---\n\n#### `falsy` \n  \n```hs\n(value: T) =\u003e value is PickFalsy\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/truthy.ts#L5)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/truthy.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Checks if `value` is falsy. Literal types are narrowed accordingly.\n\n\n#### Import\n\n```ts\n/* Node: */  import { falsy } from \"froebel/truthy\";\n/* Deno: */  import { falsy } from \"https://deno.land/x/froebel@v0.23.2/truthy.ts\";\n```\n\n\n\n\n---\n\n#### `nullish` \n  \n```hs\n(value: T) =\u003e value is Nullish\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/nullish.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/nullish.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Checks if `value` is nullish. Literal types are narrowed accordingly.\n\n\n#### Import\n\n```ts\n/* Node: */  import { nullish } from \"froebel/nullish\";\n/* Deno: */  import { nullish } from \"https://deno.land/x/froebel@v0.23.2/nullish.ts\";\n```\n\n\n\n\n---\n\n#### `notNullish` \n  \n```hs\n(value: null | T) =\u003e value is T\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/nullish.ts#L20)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/nullish.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Checks if `value` is not nullish. Literal types are narrowed accordingly.\n\n\n#### Import\n\n```ts\n/* Node: */  import { notNullish } from \"froebel/nullish\";\n/* Deno: */  import { notNullish } from \"https://deno.land/x/froebel@v0.23.2/nullish.ts\";\n```\n\n\n\n\n#### Example\n```ts\nconst nums = (...values: (number | undefined)[]): number[] =\u003e values.filter(notNullish)\n```\n\n---\n\n#### `isFulfilled` \n  \n```hs\n(result: PromiseSettledResult\u003cT\u003e) =\u003e result is PromiseFulfilledResult\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/settled.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/settled.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Checks if `result` (returned from `Promise.allSettled`) is fulfilled.\n\n\n#### Import\n\n```ts\n/* Node: */  import { isFulfilled } from \"froebel/settled\";\n/* Deno: */  import { isFulfilled } from \"https://deno.land/x/froebel@v0.23.2/settled.ts\";\n```\n\n\n\n\n---\n\n#### `isRejected` \n  \n```hs\n(result: PromiseSettledResult\u003cunknown\u003e) =\u003e result is PromiseRejectedResult\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/settled.ts#L7)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/settled.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Checks if `result` (returned from `Promise.allSettled`) is rejected.\n\n\n#### Import\n\n```ts\n/* Node: */  import { isRejected } from \"froebel/settled\";\n/* Deno: */  import { isRejected } from \"https://deno.land/x/froebel@v0.23.2/settled.ts\";\n```\n\n\n\n## String\n\n#### `prefix` \n  \n```hs\n(prefix: T0, str: T1, caseMod?: C) =\u003e `${string}`\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/prefix.ts#L12)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/prefix.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Returns `str` prefixed with `prefix`. Optionally, allows prefxing in camel\n\u003e case, i.e. `prefix('foo', 'bar', 'camel') =\u003e 'fooBar'`, or snake case, i.e.\n\u003e `prefix('foo', 'bar', 'snake') =\u003e 'foo_bar'`.\n\u003e \n\u003e The result is strictly typed, so `prefix('foo', 'bar')` will return the type\n\u003e `'foobar'`, not just a generic `string`.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import prefix from \"froebel/prefix\";\n/* Deno: */  import prefix from \"https://deno.land/x/froebel@v0.23.2/prefix.ts\";\n```\n\n\n\n\n---\n\n#### `suffix` \n  \n```hs\n(str: T1, suffix: T0, caseMod?: C) =\u003e `${string}`\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/suffix.ts#L8)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/suffix.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Returns `str` suffixed with `suffix`. Same case and type behavior as\n\u003e [prefix](#prefix).\n\n\n#### Import\n\n```ts\n/* Node: */  import suffix from \"froebel/suffix\";\n/* Deno: */  import suffix from \"https://deno.land/x/froebel@v0.23.2/suffix.ts\";\n```\n\n\n\n\n---\n\n#### `surround` \n  \n```hs\n(str: A, surrounding: B) =\u003e B extends \"\" ? A : Surround\u003cA, B\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/surround.ts#L13)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/surround.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Surrounds the `str` with `surrounding`. `surrounding` must have an even length.\n\n\n#### Import\n\n```ts\n/* Node: */  import { surround } from \"froebel/surround\";\n/* Deno: */  import { surround } from \"https://deno.land/x/froebel@v0.23.2/surround.ts\";\n```\n\n\n\n\n#### Example\n```ts\nsurround(\"foo\", \"()\")      // \"(foo)\"\nsurround(\"foo\", \"({[]})\")  // \"({[foo]})\"\n```\n\n---\n\n#### `capitalize` \n  \n```hs\n(str: T) =\u003e Capitalize\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L12)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Upper-case first letter of string.\n\n\n#### Import\n\n```ts\n/* Node: */  import { capitalize } from \"froebel/case\";\n/* Deno: */  import { capitalize } from \"https://deno.land/x/froebel@v0.23.2/case.ts\";\n```\n\n\n\n\n---\n\n#### `uncapitalize` \n  \n```hs\n(str: T) =\u003e Uncapitalize\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L16)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Lower-case first letter of string\n\n\n#### Import\n\n```ts\n/* Node: */  import { uncapitalize } from \"froebel/case\";\n/* Deno: */  import { uncapitalize } from \"https://deno.land/x/froebel@v0.23.2/case.ts\";\n```\n\n\n\n\n---\n\n#### `upper` \n  \n```hs\n(str: T) =\u003e Uppercase\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L20)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Strictly typed `String.toUpperCase()`.\n\n\n#### Import\n\n```ts\n/* Node: */  import { upper } from \"froebel/case\";\n/* Deno: */  import { upper } from \"https://deno.land/x/froebel@v0.23.2/case.ts\";\n```\n\n\n\n\n---\n\n#### `lower` \n  \n```hs\n(str: T) =\u003e Lowercase\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L24)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Strictly typed `String.toLowerCase()`.\n\n\n#### Import\n\n```ts\n/* Node: */  import { lower } from \"froebel/case\";\n/* Deno: */  import { lower } from \"https://deno.land/x/froebel@v0.23.2/case.ts\";\n```\n\n\n\n\n---\n\n#### `snake` \n  \n```hs\n(str: T) =\u003e DelimitedCase\u003cT, \"_\"\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L40)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Transforms a variable name to snake case.\n\u003e \n\u003e Note: The rules for transforming anything to snake case are somewhat vague.\n\u003e So use this only for very simple names where the resulting value is\n\u003e absolutely unambiguous. For more examples of how names are transformed, have\n\u003e a look at the test cases.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import { snake } from \"froebel/case\";\n/* Deno: */  import { snake } from \"https://deno.land/x/froebel@v0.23.2/case.ts\";\n```\n\n\n\n\n#### Example\n```ts\nsnake('fooBar') // 'foo_bar'\n```\n\n---\n\n#### `kebab` \n  \n```hs\n(str: T) =\u003e DelimitedCase\u003cT, \"-\"\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L64)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Transforms a variable name to kebab case.\n\u003e \n\u003e Note: The rules for transforming anything to kebab case are somewhat vague.\n\u003e So use this only for very simple names where the resulting value is\n\u003e absolutely unambiguous. For more examples of how names are transformed, have\n\u003e a look at the test cases.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import { kebab } from \"froebel/case\";\n/* Deno: */  import { kebab } from \"https://deno.land/x/froebel@v0.23.2/case.ts\";\n```\n\n\n\n\n#### Example\n```ts\nkebab('fooBar') // 'foo-bar'\n```\n\n---\n\n#### `camel` \n  \n```hs\n(str: T) =\u003e CamelCase\u003cT\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L88)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Transforms a variable name to camel case.\n\u003e \n\u003e Note: The rules for transforming anything to camel case are somewhat vague.\n\u003e So use this only for very simple names where the resulting value is\n\u003e absolutely unambiguous. For more examples of how names are transformed, have\n\u003e a look at the test cases.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import { camel } from \"froebel/case\";\n/* Deno: */  import { camel } from \"https://deno.land/x/froebel@v0.23.2/case.ts\";\n```\n\n\n\n\n#### Example\n```ts\ncamel('foo_bar') // 'fooBar'\n```\n\n---\n\n#### `pascal` \n  \n```hs\n(str: T) =\u003e Capitalize\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L109)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Transforms a variable name to pascal case.\n\u003e \n\u003e Note: The rules for transforming anything to pascal case are somewhat vague.\n\u003e So use this only for very simple names where the resulting value is\n\u003e absolutely unambiguous. For more examples of how names are transformed, have\n\u003e a look at the test cases.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import { pascal } from \"froebel/case\";\n/* Deno: */  import { pascal } from \"https://deno.land/x/froebel@v0.23.2/case.ts\";\n```\n\n\n\n\n#### Example\n```ts\npascal('foo_bar') // 'FooBar'\n```\n\n---\n\n#### `screamingSnake` \n  \n```hs\n(str: T) =\u003e Uppercase\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L122)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Transforms a variable name to screaming snake case.\n\u003e \n\u003e \u003csub\u003esee [snake](#snake)\u003c/sub\u003e\n\n\n#### Import\n\n```ts\n/* Node: */  import { screamingSnake } from \"froebel/case\";\n/* Deno: */  import { screamingSnake } from \"https://deno.land/x/froebel@v0.23.2/case.ts\";\n```\n\n\n\n\n#### Example\n```ts\nscreamingSnake('fooBar') // 'FOO_BAR'\n```\n\n---\n\n#### `transformCase` \n  \n```hs\n(str: T, targetCase: C) =\u003e DelimitedCase\u003cT, \"_\"\u003e\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/case.ts#L135)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/case.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Transform a variable name to `targetCase`\n\u003e \n\u003e \u003csub\u003esee [snake](#snake), [kebab](#kebab), [camel](#camel), [pascal](#pascal), and [screamingSnake](#screamingsnake)\u003c/sub\u003e\n\n\n#### Import\n\n```ts\n/* Node: */  import { transformCase } from \"froebel/case\";\n/* Deno: */  import { transformCase } from \"https://deno.land/x/froebel@v0.23.2/case.ts\";\n```\n\n\n\n## Math\n\n#### `clamp` \n  \n```hs\n(min: number, num: number, max: number) =\u003e number\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/clamp.ts#L2)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/clamp.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Clamp `num` between `min` and `max` inclusively.\n\n\n#### Import\n\n```ts\n/* Node: */  import clamp from \"froebel/clamp\";\n/* Deno: */  import clamp from \"https://deno.land/x/froebel@v0.23.2/clamp.ts\";\n```\n\n\n\n## Data Structures\n\n#### `BiMap` \n  \n```hs\nclass BiMap\u003cL, R\u003e(data?: Map\u003cL, R\u003e | [L, R][], aliasLeft?: AL, aliasRight?: AR)\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/bimap.ts#L172)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/bimap.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Bidirectional map. Maps two sets of keys in a one-to-one relation.\n\u003e \n\u003e Both sides are accessible (at .left \u0026 .right, or at their respective alias if\n\u003e one was provided in the constructor) with an interface similar to that of the\n\u003e built-in Map and the same iteration behavior.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import BiMap from \"froebel/bimap\";\n/* Deno: */  import BiMap from \"https://deno.land/x/froebel@v0.23.2/bimap.ts\";\n```\n\n\n\n\n#### Examples\n```ts\nconst nums = BiMap.from({ one: 1, two: 2 })\n\n// different ways of iterating over the entries\n[...nums.left]                 // [['one',1], ['two',2]]\n[...nums.right]                // [[1,'one'], [2,'two']]\n[...nums.left.keys()]          // ['one', 'two']\n[...nums.left.values()]        // [1, 2]\n[...nums.right.keys()]         // [1, 2]\n[...nums.right.values()]       // ['one', 'two']\n[...nums]                      // [['one',1], ['two',2]]\n[...nums.right.entries()]      // [[1,'one'], [2,'two']]\nObject.fromEntries(nums.right) // { '1': 'one', '2': 'two' }\n\n// setting a value\nnums.left.three = 3\n// when accessing a property using bracket notation (i.e. nums.right[4]),\n// JavaScript coerces the key to a string, so keys that aren't strings or\n// symbols must be accessed using the same access methods known from Map.\nnums.right.set(4, 'four')\n\n// remapping values\nnums.left.tres = 3          // {one: 1, two: 2, tres: 3, four: 4}\nnums.right.set(4, 'cuatro') // {one: 1, two: 2, tres: 3, cuatro: 4}\n\n// deleting\ndelete nums.left.tres    // {one: 1, two: 2, cuatro: 4}\nnums.right.delete(4)     // {one: 1, two: 2}\n\n// reversing the map\nconst num2Name = nums.reverse()\nconsole.log([...num2Name.left])                 // [[1,'one'], [2,'two']]\nconsole.log(Object.fromEntries(num2Name.right)) // {one: 1, two: 2}\n\n// other methods known from built-in Map\nnums.size               // 2\nnums.[left|right].size  // 2\nnums.clear() // equivalent to nums.[left|right].clear()\nconsole.log(nums.size)  // 0\n```\n\n```ts\n// giving aliases to both sides\nconst dictionary = new BiMap(\n  [\n    ['hello', 'hallo'],\n    ['bye', 'tschüss'],\n  ],\n  'en',\n  'de'\n)\n\ndictionary.de.get('hallo') // 'hello'\ndictionary.en.get('bye')   // 'tschüss'\n\ndelete dictionary.de.hallo\nconsole.log(Object.fromEntries(dictionary.en)) // { bye: 'tschüss' }\n\n// you can also use the BiMap.alias method:\nBiMap.alias('en', 'de')\u003cstring, string\u003e()\nBiMap.alias('en', 'de')([['hello', 'hallo']])\nBiMap.alias('en', 'de')(new Map\u003cstring, string\u003e())\nBiMap.alias('en', 'de')({ hello: 'hallo' })\nBiMap.alias('en', 'de')(new Set(['hello']), new Set(['hallo']))\n\n// the same arguments can be used with BiMap.from, e.g.:\nBiMap.from(new Set\u003cnumber\u003e(), new Set\u003cnumber\u003e())\n```\n\n---\n\n#### `SortedArray` \n  \n```hs\nclass SortedArray\u003cT\u003e(compare: Cmp\u003cT\u003e, ...value: T[])\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/sortedArray.ts#L134)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/sortedArray.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Sorted array. Behaves much like a regular array but its elements remain\n\u003e sorted using the `compare` function supplied in the constructor.\n\u003e \n\u003e Contains most of the methods defined on regular JavaScript arrays as long as\n\u003e they don't modify the array's content in place.\n\u003e \n\u003e New elements are added using the `add(...values)` method.\n\u003e \n\u003e Elements can still be accessed using bracket notation as in plain JavaScript\n\u003e arrays but can't be assigned to using bracket notation (as that could change\n\u003e the element's sort position).\n\u003e \n\u003e Elements can be removed using the `delete(...indices)` method, which returns\n\u003e an array containing the deleted values.\n\u003e Deleting an element using `delete sorted[index]` will also work, but results\n\u003e in a TypeScript error because element access is marked readonly.\n\u003e \n\u003e Array methods that pass a reference of the array to a callback (e.g. `map`,\n\u003e `reduce`, `find`) will pass a reference to the SortedArray instance instead.\n\u003e \n\u003e The `filter` and `slice` methods will return SortedArray instances instead of\n\u003e plain arrays.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import SortedArray from \"froebel/sortedArray\";\n/* Deno: */  import SortedArray from \"https://deno.land/x/froebel@v0.23.2/sortedArray.ts\";\n```\n\n\n\n\n---\n\n#### `SortedMap` \n  \n```hs\nclass SortedMap\u003cK, V\u003e(compare: Cmp\u003cK, V\u003e, entries?: null | [K, V][])\n```\n\n\u003csup\u003e\u003csup\u003e_[source](https://github.com/MathisBullinger/froebel/blob/main/sortedMap.ts#L11)_ | _[tests](https://github.com/MathisBullinger/froebel/blob/main/sortedMap.test.ts)_\u003c/sup\u003e\u003c/sup\u003e\n\n\u003e Behaves like a regular JavaScript `Map`, but its iteration order is dependant\n\u003e on the `compare` function supplied in the constructor.\n\u003e \n\u003e Note: The item's sort position is only computed automatically on insertion.\n\u003e If you update one of the values that the `compare` function depends on, you\n\u003e must call the `update(key)` method afterwards to ensure the map stays sorted.\n\u003e \n\n\n#### Import\n\n```ts\n/* Node: */  import SortedMap from \"froebel/sortedMap\";\n/* Deno: */  import SortedMap from \"https://deno.land/x/froebel@v0.23.2/sortedMap.ts\";\n```\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathisbullinger%2Ffroebel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmathisbullinger%2Ffroebel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathisbullinger%2Ffroebel/lists"}