https://github.com/MathisBullinger/froebel Skip to content Sign up * Product + Features + Mobile + Actions + Codespaces + Copilot + Packages + Security + Code review + Issues + Discussions + Integrations + GitHub Sponsors + Customer stories * Team * Enterprise * Explore + Explore GitHub + Learn and contribute + Topics + Collections + Trending + Skills + GitHub Sponsors + Open source guides + Connect with others + The ReadME Project + Events + Community forum + GitHub Education + GitHub Stars program * Marketplace * Pricing + Plans + Compare plans + Contact Sales + Education [ ] * # In this repository All GitHub | Jump to | * No suggested jump to results * # In this repository All GitHub | Jump to | * # In this user All GitHub | Jump to | * # In this repository All GitHub | Jump to | Sign in Sign up {{ message }} MathisBullinger / froebel Public * Notifications * Fork 8 * Star 491 A strictly typed utility library. www.npmjs.com/package/froebel License ISC license 491 stars 8 forks Star Notifications * Code * Issues 0 * Pull requests 1 * Discussions * Actions * Security * Insights More * Code * Issues * Pull requests * Discussions * Actions * Security * Insights MathisBullinger/froebel This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. main Switch branches/tags [ ] Branches Tags Could not load branches Nothing to show {{ refName }} default View all branches Could not load tags Nothing to show {{ refName }} default View all tags 3 branches 33 tags Code * Clone HTTPS GitHub CLI [https://github.com/M] Use Git or checkout with SVN using the web URL. [gh repo clone Mathis] Work fast with our official CLI. Learn more. * Open with GitHub Desktop * Download ZIP Launching GitHub Desktop If nothing happens, download GitHub Desktop and try again. Launching GitHub Desktop If nothing happens, download GitHub Desktop and try again. Launching Xcode If nothing happens, download Xcode and try again. Launching Visual Studio Code Your codespace will open once ready. There was a problem preparing your codespace, please try again. Latest commit @MathisBullinger MathisBullinger pipe function ... adcb8a2 Aug 7, 2022 pipe function adcb8a2 Git stats * 116 commits Files Permalink Failed to load latest commit information. Type Name Latest commit message Commit time .githooks ci: add pre-commit hook Jun 12, 2022 .github ci: update deno version Aug 6, 2022 .vscode migrate to deno May 26, 2022 npm pipe function Aug 7, 2022 scripts fix typo in readme Jun 14, 2022 .gitignore ci: add pre-commit hook Jun 12, 2022 LICENSE add license Dec 17, 2021 README.md pipe function Aug 7, 2022 atWrap.test.ts chore: restructure project files May 26, 2022 atWrap.ts chore: restructure project files May 26, 2022 batch.test.ts chore: restructure project files May 26, 2022 batch.ts chore: restructure project files May 26, 2022 bimap.test.ts chore: restructure project files May 26, 2022 bimap.ts chore: restructure project files May 26, 2022 bundle.test.ts chore: restructure project files May 26, 2022 bundle.ts chore: restructure project files May 26, 2022 callAll.test.ts chore: restructure project files May 26, 2022 callAll.ts chore: restructure project files May 26, 2022 case.test.ts convert to screaming snake case Aug 6, 2022 case.ts convert to screaming snake case Aug 6, 2022 clamp.test.ts chore: restructure project files May 26, 2022 clamp.ts chore: restructure project files May 26, 2022 clone.test.ts chore: restructure project files May 26, 2022 clone.ts use structuredClone if available Jun 15, 2022 debounce.test.ts chore: restructure project files May 26, 2022 debounce.ts chore: restructure project files May 26, 2022 deno.json migrate to deno May 26, 2022 ds.ts chore: restructure project files May 26, 2022 equal.test.ts chore: restructure project files May 26, 2022 equal.ts chore: restructure project files May 26, 2022 equality.ts chore: restructure project files May 26, 2022 error.ts chore: restructure project files May 26, 2022 except.ts chore: restructure project files May 26, 2022 forward.test.ts chore: restructure project files May 26, 2022 forward.ts doc: fix forward example typo May 26, 2022 function.ts pipe function Aug 7, 2022 ident.test.ts chore: restructure project files May 26, 2022 ident.ts chore: restructure project files May 26, 2022 import_map.json update import map May 26, 2022 invoke.test.ts chore: restructure project files May 26, 2022 invoke.ts chore: restructure project files May 26, 2022 isPromise.test.ts chore: restructure project files May 26, 2022 isPromise.ts chore: restructure project files May 26, 2022 iterable.ts chore: restructure project files May 26, 2022 list.ts shuffle & shuffleInPlace Jun 18, 2022 map.test.ts map object Jul 9, 2022 map.ts map object Jul 9, 2022 math.ts chore: restructure project files May 26, 2022 memoize.test.ts chore: restructure project files May 26, 2022 memoize.ts chore: restructure project files May 26, 2022 mod.ts promisify May 28, 2022 noop.test.ts promisify May 28, 2022 noop.ts createQueue May 27, 2022 nullish.ts chore: restructure project files May 26, 2022 nullishChain.test.ts chore: restructure project files May 26, 2022 nullishChain.ts chore: restructure project files May 26, 2022 object.ts map object Jul 9, 2022 omit.test.ts chore: restructure project files May 26, 2022 omit.ts chore: restructure project files May 26, 2022 oneOf.test.ts chore: restructure project files May 26, 2022 oneOf.ts chore: restructure project files May 26, 2022 partial.test.ts chore: restructure project files May 26, 2022 partial.ts chore: restructure project files May 26, 2022 partition.test.ts chore: restructure project files May 26, 2022 partition.ts map object Jul 9, 2022 path.ts chore: restructure project files May 26, 2022 pick.test.ts chore: restructure project files May 26, 2022 pick.ts chore: restructure project files May 26, 2022 pipe.test.ts pipe function Aug 7, 2022 pipe.ts pipe function Aug 7, 2022 predicate.ts chore: restructure project files May 26, 2022 prefix.test.ts chore: restructure project files May 26, 2022 prefix.ts chore: restructure project files May 26, 2022 promise.ts promisify May 28, 2022 promisify.test.ts promisify May 28, 2022 promisify.ts include license in npm package May 28, 2022 queue.test.ts createQueue May 27, 2022 queue.ts createQueue May 27, 2022 range.test.ts chore: restructure project files May 26, 2022 range.ts chore: restructure project files May 26, 2022 repeat.test.ts chore: restructure project files May 26, 2022 repeat.ts update readme May 26, 2022 select.test.ts support maps in select May 27, 2022 select.ts rename pick -> select May 27, 2022 settled.test.ts chore: restructure project files May 26, 2022 settled.ts chore: restructure project files May 26, 2022 shuffle.test.ts shuffle & shuffleInPlace Jun 18, 2022 shuffle.ts shuffle & shuffleInPlace Jun 18, 2022 sortedArray.test.ts chore: restructure project files May 26, 2022 sortedArray.ts chore: restructure project files May 26, 2022 sortedMap.test.ts chore: restructure project files May 26, 2022 sortedMap.ts chore: restructure project files May 26, 2022 string.ts surround Jun 12, 2022 suffix.test.ts chore: restructure project files May 26, 2022 suffix.ts chore: restructure project files May 26, 2022 surround.test.ts surround Jun 12, 2022 surround.ts surround Jun 12, 2022 take.test.ts chore: restructure project files May 26, 2022 take.ts chore: restructure project files May 26, 2022 throttle.test.ts chore: restructure project files May 26, 2022 throttle.ts doc: link to tests May 27, 2022 truthy.test.ts chore: restructure project files May 26, 2022 truthy.ts chore: restructure project files May 26, 2022 types.test.ts convert to screaming snake case Aug 6, 2022 types.ts convert to screaming snake case Aug 6, 2022 unzip.test.ts chore: restructure project files May 26, 2022 unzip.ts chore: restructure project files May 26, 2022 zip.test.ts chore: restructure project files May 26, 2022 zip.ts chore: restructure project files May 26, 2022 View code [ ] Froebel - a strictly typed TypeScript utility library. Installation Using npm Using Deno Available Utilities Table of Contents Function ident Import noop Import partial Import Example forward Import Examples callAll Import Example pipe Import Example bundle Import bundleSync Import nullishChain Import Example asyncNullishChain Import Example throttle Import debounce Import memoize Import Examples limitInvocations Import once Import List atWrap Import zip Import Example zipWith Import Example unzip Import Example unzipWith Import Example batch Import Example partition Import Example shuffle Import shuffleInPlace Import take Import Example range Import numberRange Import Example alphaRange Import Example Iterable repeat Import Example take Import Example Object pick Import Example omit Import Example map Import Examples Path select Import Example Equality oneOf Import equal Import clone Import Promise promisify Import Examples createQueue Import Example isPromise Import isNotPromise Import Example Predicate truthy Import falsy Import nullish Import notNullish Import Example isFulfilled Import isRejected Import String prefix Import suffix Import surround Import Example capitalize Import uncapitalize Import upper Import lower Import snake Import Example kebab Import Example camel Import Example pascal Import Example screamingSnake Import Example transformCase Import Math clamp Import Data Structures BiMap Import Examples SortedArray Import SortedMap Import README.md 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 or npm. Installation Using npm npm install froebel and -- assuming a module-compatible system like webpack -- import as: import { someUtility } from 'froebel'; // you can also import the utility you need directly: import memoize from 'froebel/memoize'; Using Deno import { someUtility } from "https://deno.land/x/froebel@v0.20.0/mod.ts"; // or import just the utility you need: import memoize from "https://deno.land/x/froebel@v0.20.0/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 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 + noop + partial + forward + callAll + pipe + bundle + bundleSync + nullishChain + asyncNullishChain + throttle + debounce + memoize + limitInvocations + once * list + atWrap + zip + zipWith + unzip + unzipWith + batch + partition + shuffle + shuffleInPlace + take + range + numberRange + alphaRange * iterable + repeat + take * object + pick + omit + map * path + select * equality + oneOf + equal + clone * promise + promisify + createQueue + isPromise + isNotPromise * predicate + truthy + falsy + nullish + notNullish + isFulfilled + isRejected * string + prefix + suffix + surround + capitalize + uncapitalize + upper + lower + snake + kebab + camel + pascal + screamingSnake + transformCase * math + clamp * data structures + BiMap + SortedArray + SortedMap Function ident (value: T) => T ^^source | tests Identity function. Import /* Node: */ import ident from "froebel/ident"; /* Deno: */ import ident from "https://deno.land/x/froebel@v0.20.0/ident.ts"; --------------------------------------------------------------------- noop () => void ^^source | tests Import /* Node: */ import noop from "froebel/noop"; /* Deno: */ import noop from "https://deno.land/x/froebel@v0.20.0/noop.ts"; --------------------------------------------------------------------- partial (fun: T, ...argsLeft: PL) => (...argsRight: PR) => ReturnType ^^source | tests Partially apply a function. Import /* Node: */ import partial from "froebel/partial"; /* Deno: */ import partial from "https://deno.land/x/froebel@v0.20.0/partial.ts"; Example const divide = (dividend: number, divisor: number) => dividend / divisor // (divisor: number) => number const oneOver = partial(divide, 1) // prints: 0.25 console.log(oneOver(4)) --------------------------------------------------------------------- forward (fun: T, ...argsRight: PR) => (...argsLeft: PL) => ReturnType ^^source | tests Given a function and its nth..last arguments, return a function accepting arguments 0..n-1. Import /* Node: */ import forward from "froebel/forward"; /* Deno: */ import forward from "https://deno.land/x/froebel@v0.20.0/forward.ts"; Examples const divide = (dividend: number, divisor: number) => dividend / divisor // (dividend: number) => number const divideBy2 = forward(divide, 2) // prints: 0.5 console.log(divideBy2(1)) const fetchUrl = async (protocol: string, domain: string, path: string) => await fetch(`${protocol}://${domain}/${path}`) const fetchRepo = forward(fetchUrl, 'github.com', 'MathisBullinger/froebel') const viaHTTPS = await fetchRepo('https') --------------------------------------------------------------------- callAll (funs: F[], ...args: P) => ReturnTypes ^^source | tests Take a list of functions that accept the same parameters and call them all with the provided arguments. Import /* Node: */ import callAll from "froebel/callAll"; /* Deno: */ import callAll from "https://deno.land/x/froebel@v0.20.0/callAll.ts"; Example const mult = (a: number, b: number) => a * b const div = (a: number, b: number) => a / b // prints: [8, 2] console.log( callAll([mult, div], 4, 2) ) --------------------------------------------------------------------- pipe (...funs: T) => PipedFun ^^source | tests 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 /* Node: */ import pipe from "froebel/pipe"; /* Deno: */ import pipe from "https://deno.land/x/froebel@v0.20.0/pipe.ts"; Example 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 --------------------------------------------------------------------- bundle (...funs: l[]) => (...args: T) => Promise ^^source | tests Given a list of functions that accept the same parameters, returns a function that takes these parameters and invokes all of the given functions. The returned function returns a promise that resolves once all functions returned/resolved and rejects if any of the functions throws/rejects - but only after all returned promises have been settled. Import /* Node: */ import bundle from "froebel/bundle"; /* Deno: */ import bundle from "https://deno.land/x/froebel@v0.20.0/bundle.ts"; --------------------------------------------------------------------- bundleSync (...funs: l[]) => (...args: T) => void ^^source | tests Same as bundle, but return synchronously. If any of the functions throws an error synchronously, none of the functions after it will be invoked and the error will propagate. Import /* Node: */ import { bundleSync } from "froebel/bundle"; /* Deno: */ import { bundleSync } from "https://deno.land/x/froebel@v0.20.0/bundle.ts"; --------------------------------------------------------------------- nullishChain (...funs: [] | [FF, ...FR[]]) => (...args: Parameters) => ReturnType | ReturnType ^^source | tests Given a list of functions that accept the same parameters, returns a function that given these arguments returns the result of the first function whose result is not nullish. This is equivalent to chaining together invocations of the passed in functions with the given arguments with nullish coalescing (??) operators. Import /* Node: */ import { nullishChain } from "froebel/nullishChain"; /* Deno: */ import { nullishChain } from "https://deno.land/x/froebel@v0.20.0/nullishChain.ts"; Example const isAdult = (age: number) => { if (n >= 18) return 'adult' } const isToddler = (age: number) => { if (n <= 3) return 'toddler' } const ageGroup = nullishChain(isAdult, isToddler, () => 'child') // this is functionally equivalent to: const ageGroup = age => isAdult(age) ?? isToddler(age) ?? 'child' ageGroup(1) // prints: 'toddler' ageGroup(10) // prints: 'child' ageGroup(50) // prints: 'adult' --------------------------------------------------------------------- asyncNullishChain (...funs: [] | [FF, ...FR[]]) => (...args: Parameters) => Promise> | PromType>> ^^source | tests Same as nullishChain but accept asynchronous functions too. Import /* Node: */ import { asyncNullishChain } from "froebel/nullishChain"; /* Deno: */ import { asyncNullishChain } from "https://deno.land/x/froebel@v0.20.0/nullishChain.ts"; Example const readFromCache = (id: string): Resource => { if (id in cache) return cache[id] } const readFromFile = (id: string): Resource => { if (fileExists(id)) return readFile(id) } const fetchFromNet = async (id: string): Promise => await fetch(`someURL/${id}`) // async (id: string) => Promise const getResource = asyncNullishChain(readFromCache, readFromFile, fetchFromNet) --------------------------------------------------------------------- throttle (fun: T, ms: number, opts?: {leading: boolean, trailing: boolean}) => l, void> & {[cancel]: () => void} ^^source | tests 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 /* Node: */ import throttle from "froebel/throttle"; /* Deno: */ import throttle from "https://deno.land/x/froebel@v0.20.0/throttle.ts"; --------------------------------------------------------------------- debounce (fun: T, ms: number) => l, void> & {[cancel]: () => void} ^^source | tests Creates a debounced function that delays invoking fun until ms milliseconds have passed since the last invocation of the debounced function. fun is invoked with the last arguments passed to the debounced function. Calling [debounce.cancel]() on the debounced function will cancel the currently scheduled invocation. Import /* Node: */ import debounce from "froebel/debounce"; /* Deno: */ import debounce from "https://deno.land/x/froebel@v0.20.0/debounce.ts"; --------------------------------------------------------------------- memoize (fun: T, opt: {limit: number, weak: W, key: (...args: Parameters) => K}) => T & {cache: W extends false ? Map> : Cache>} ^^source | tests Returns a copy of fun that remembers its result for any given arguments and only invokes fun for unknown arguments. The cache key is computed using the key function. The default key function simply stringifies the arguments. If limit is specified, only the limit-last entries are kept in cache. The function's cache is available at memoized.cache. If opt.weak is true, non-primitive cache keys are stored in a WeakMap. This behavior might for example be useful if you want to memoize some calculation including a DOM Node without holding on to a reference of that node. Using weak keys prohibits setting a limit. Import /* Node: */ import memoize from "froebel/memoize"; /* Deno: */ import memoize from "https://deno.land/x/froebel@v0.20.0/memoize.ts"; Examples const expensiveCalculation = (a: number, b: number) => { console.log(`calculate ${a} + ${b}`) return a + b } const calc = memoize(expensiveCalculation) console.log( calc(1, 2) ) // calculate 1 + 2 // 3 console.log( calc(20, 5) ) // calculate 20 + 5 // 25 console.log( calc(20, 5) ) // 25 console.log( calc(1, 2) ) // 3 calc.cache.clear() console.log( calc(1, 2) ) // calculate 1 + 2 // 3 const logIfDifferent = memoize( (msg: string) => console.log(msg), { limit: 1, key: msg => msg } ) logIfDifferent('a') logIfDifferent('a') logIfDifferent('b') logIfDifferent('a') // a // b // a --------------------------------------------------------------------- limitInvocations (fun: T, limit: number, ...funs: ExcS) => T ^^source | tests 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 /* Node: */ import { limitInvocations } from "froebel/invoke"; /* Deno: */ import { limitInvocations } from "https://deno.land/x/froebel@v0.20.0/invoke.ts"; --------------------------------------------------------------------- once (fun: T, ...funs: ExcS) => T ^^source | tests Special case of limitInvocations. fun can only be invoked once. [see limitInvocations] Import /* Node: */ import { once } from "froebel/invoke"; /* Deno: */ import { once } from "https://deno.land/x/froebel@v0.20.0/invoke.ts"; List atWrap (arr: T[], i: number) => T ^^source | tests Access list at i % length. Negative indexes start indexing the last element as [-1] and wrap around to the back. Import /* Node: */ import atWrap from "froebel/atWrap"; /* Deno: */ import atWrap from "https://deno.land/x/froebel@v0.20.0/atWrap.ts"; --------------------------------------------------------------------- zip (...lists: T) => Zip ^^source | tests Takes multiple lists and returns a list of tuples containing the value in each list at the current index. If the lists are of different lengths, the returned list of tuples has the length of the shortest passed in list. Import /* Node: */ import zip from "froebel/zip"; /* Deno: */ import zip from "https://deno.land/x/froebel@v0.20.0/zip.ts"; Example const pairs = zip([1,2,3], ['a','b','c']) console.log(pairs) // prints: [[1,'a'], [2,'b'], [3,'c']] --------------------------------------------------------------------- zipWith (zipper: (...args: {[I in string | number | symbol]: U}) => U, ...lists: T) => U[] ^^source | tests Same as zip but also takes a zipper function, that is called for each index with the element at current index in each list as arguments. The result of zipper is the element at current index in the list returned from zipWith. Import /* Node: */ import { zipWith } from "froebel/zip"; /* Deno: */ import { zipWith } from "https://deno.land/x/froebel@v0.20.0/zip.ts"; Example const sums = zipWith((a,b) => a+b, [1,2,3], [4,5,6]) console.log(sums) // prints: [5,7,9] --------------------------------------------------------------------- unzip (...zipped: T[][]) => Unzip ^^source | tests Reverse of zip. Takes a list of tuples and deconstructs them into an array (of length of the tuples length) of lists each containing all the elements in all tuples at the lists index. Import /* Node: */ import unzip from "froebel/unzip"; /* Deno: */ import unzip from "https://deno.land/x/froebel@v0.20.0/unzip.ts"; Example const [nums, chars] = unzip([1,'a'], [2,'b'], [3,'c']) console.log(nums) // prints: [1, 2, 3] console.log(chars) // prints: ['a','b','c'] --------------------------------------------------------------------- unzipWith (zipped: T[][], ...unzippers: U) => {[I in string | number | symbol]: ReturnType} ^^source | tests Same as unzip but accepts an unzipper function for each tuple index. The unzipper's return value is used as the value in the list at that index returned from unzipWith. The unzipper takes the current element as its first argument, an accumulator as second argument (initially undefined) and its return value is the accumulator passed into the next invocation. Import /* Node: */ import { unzipWith } from "froebel/unzip"; /* Deno: */ import { unzipWith } from "https://deno.land/x/froebel@v0.20.0/unzip.ts"; Example const [nums, str] = unzip( [ [1,'a'], [2,'b'], [3,'c'] ], (n, acc: number[] = []) => [...acc, n], (c, str = '') => str + c ) console.log(nums) // prints: [1, 2, 3] console.log(str) // prints: 'abc' --------------------------------------------------------------------- batch (list: T[], batchSize: number) => T[][] ^^source | tests Takes a list and returns it in multiple smaller lists of the size batchSize. The last batch may be smaller than batchSize depending on if list size is divisible by batchSize. Import /* Node: */ import batch from "froebel/batch"; /* Deno: */ import batch from "https://deno.land/x/froebel@v0.20.0/batch.ts"; Example batch([1,2,3,4,5], 2) // -> [ [1,2], [3,4], [5] ] --------------------------------------------------------------------- partition (list: T[], predicate: (el: T) => el is S) => [S[], Exclude[]] ^^source | tests Takes a list and returns a pair of lists containing: the elements that match the predicate and those that don't, respectively. Think of it as filter, but the elements that don't pass the filter aren't discarded but returned in a separate list instead. Import /* Node: */ import partition from "froebel/partition"; /* Deno: */ import partition from "https://deno.land/x/froebel@v0.20.0/partition.ts"; Example const [strings, numbers] = partition( ['a', 'b', 1, 'c', 2, 3], (el): el is string => typeof el === 'string' ) // strings: ["a", "b", "c"] // numbers: [1, 2, 3] --------------------------------------------------------------------- shuffle (list: T[]) => T[] ^^source | tests Shuffles list using the Fisher-Yates shuffle algorithm. The original list is not modified and the shuffled list is returned. Import /* Node: */ import shuffle from "froebel/shuffle"; /* Deno: */ import shuffle from "https://deno.land/x/froebel@v0.20.0/shuffle.ts"; --------------------------------------------------------------------- shuffleInPlace (list: unknown[]) => void ^^source | tests Same as shuffle but shuffles list in place. Import /* Node: */ import { shuffleInPlace } from "froebel/shuffle"; /* Deno: */ import { shuffleInPlace } from "https://deno.land/x/froebel@v0.20.0/shuffle.ts"; --------------------------------------------------------------------- take (n: number, list: Iterable) => T[] ^^source | tests Takes n elements from the iterable list and returns them as an array. Import /* Node: */ import { take } from "froebel/list"; /* Deno: */ import { take } from "https://deno.land/x/froebel@v0.20.0/list.ts"; Example take(5, repeat(1, 2)) // -> [1, 2, 1, 2, 1] take(3, [1, 2, 3, 4]) // -> [1, 2, 3] take(3, [1, 2]) // -> [1, 2] --------------------------------------------------------------------- range ^^source | tests Creates a range between two values. [see numberRange and alphaRange] Import /* Node: */ import range from "froebel/range"; /* Deno: */ import range from "https://deno.land/x/froebel@v0.20.0/range.ts"; --------------------------------------------------------------------- numberRange (start: number, end: number, step: number) => number[] ^^source | tests Constructs a numeric between start and end inclusively. Import /* Node: */ import { numberRange } from "froebel/range"; /* Deno: */ import { numberRange } from "https://deno.land/x/froebel@v0.20.0/range.ts"; Example range(2, 6) // -> [2, 3, 4, 5, 6] range(8, 9, .3) // -> [8, 8.3, 8.6, 8.9] range(3, -2) // -> [3, 2, 1, 0, -1, -2] --------------------------------------------------------------------- alphaRange (start: string, end: string) => string[] ^^source | tests Constructs a range between characters. Import /* Node: */ import { alphaRange } from "froebel/range"; /* Deno: */ import { alphaRange } from "https://deno.land/x/froebel@v0.20.0/range.ts"; Example range('a', 'd') // -> ['a', 'b', 'c', 'd'] range('Z', 'W') // -> ['Z', 'Y', 'X', 'W'] Iterable repeat (...sequence: [T, ...T[]]) => Generator ^^source | tests Returns a generator that repeats sequence. Import /* Node: */ import repeat from "froebel/repeat"; /* Deno: */ import repeat from "https://deno.land/x/froebel@v0.20.0/repeat.ts"; Example // prints: 1, 2, 3, 1, 2, 3, ... for (const n of repeat(1, 2, 3)) console.log(n) --------------------------------------------------------------------- take (n: number, list: Iterable) => Generator ^^source | tests Takes n elements from the iterable list and returns them as a generator. Import /* Node: */ import { take } from "froebel/iterable"; /* Deno: */ import { take } from "https://deno.land/x/froebel@v0.20.0/iterable.ts"; Example [...take(5, repeat(1, 2))] // -> [1, 2, 1, 2, 1] [...take(3, [1, 2, 3, 4])] // -> [1, 2, 3] [...take(3, [1, 2])] // -> [1, 2] Object pick (obj: T, ...keys: K[]) => Pick ^^source | tests From obj, create a new object that only includes keys. Import /* Node: */ import pick from "froebel/pick"; /* Deno: */ import pick from "https://deno.land/x/froebel@v0.20.0/pick.ts"; Example pick({ a: 1, b: 2, c: 3 }, 'a', 'c') // { a: 1, c: 3 } --------------------------------------------------------------------- omit (obj: T, ...keys: K[]) => Omit ^^source | tests From obj, create a new object that does not include keys. Import /* Node: */ import omit from "froebel/omit"; /* Deno: */ import omit from "https://deno.land/x/froebel@v0.20.0/omit.ts"; Example omit({ a: 1, b: 2, c: 3 }, 'a', 'c') // { b: 2 } --------------------------------------------------------------------- map (data: Map, callback: (key: IK, value: IV) => [OK, OV]) => Map ^^source | tests Map over data. data can be a regular object, a Map, a Set, or an array. Import /* Node: */ import map from "froebel/map"; /* Deno: */ import map from "https://deno.land/x/froebel@v0.20.0/map.ts"; Examples // -> { a: 1, b: 2 } map({ a: '1', b: '2' }, (key, value) => [key, parseInt(value)]) // -> Map([ [2, 1], [4, 3] ]) map(new Map([ [1, 2], [3, 4] ]), (key, value) => [key + 1, value - 1]) Path select (obj: T, ...path: P) => PickPath ^^source | tests Returns the value in obj at path. If the given path does not exist, the symbol none is returned. Import /* Node: */ import select from "froebel/select"; /* Deno: */ import select from "https://deno.land/x/froebel@v0.20.0/select.ts"; Example // -> 'something' select( { a: { deeply: [{ nested: { object: 'something' } }] } }, 'a', 'deeply', 0, 'nested', 'object' ) Equality oneOf (value: T, ...cmps: TT) => value is TT[number] ^^source | tests Checks if v is one of cmps. Import /* Node: */ import oneOf from "froebel/oneOf"; /* Deno: */ import oneOf from "https://deno.land/x/froebel@v0.20.0/oneOf.ts"; --------------------------------------------------------------------- equal (a: unknown, b: unknown) => boolean ^^source | tests Checks if a and b are structurally equal using the following algorithm: + primitives are compared by value + functions are compared by reference + objects (including arrays) are checked to have the same properties and their values are compared recursively using the same algorithm Import /* Node: */ import equal from "froebel/equal"; /* Deno: */ import equal from "https://deno.land/x/froebel@v0.20.0/equal.ts"; --------------------------------------------------------------------- clone (value: T) => T ^^source | tests Returns a copied version of value. If value is primitive, returns value. Otherwise, properties of value are copied recursively. Only value's own enumerable properties are cloned. Arrays are cloned by mapping over their elements. If a path in value references itself or a parent path, then in the resulting object that path will also reference the path it referenced in the original object (but now in the resuling object instead of the original). Import /* Node: */ import clone from "froebel/clone"; /* Deno: */ import clone from "https://deno.land/x/froebel@v0.20.0/clone.ts"; Promise promisify (withCallback: T, resultIndex?: N, errorIndex: null | number) => Promisified ^^source | tests 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 /* Node: */ import promisify from "froebel/promisify"; /* Deno: */ import promisify from "https://deno.land/x/froebel@v0.20.0/promisify.ts"; Examples 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) const sleep = promisify(setTimeout).callbackFirst await sleep(200) 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 () => Queue ^^source | tests 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 /* Node: */ import createQueue from "froebel/queue"; /* Deno: */ import createQueue from "https://deno.land/x/froebel@v0.20.0/queue.ts"; Example 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 (value: unknown) => value is Promise ^^source | tests Checks if value looks like a promise. Import /* Node: */ import isPromise from "froebel/isPromise"; /* Deno: */ import isPromise from "https://deno.land/x/froebel@v0.20.0/isPromise.ts"; --------------------------------------------------------------------- isNotPromise (value: T) => value is Exclude> ^^source | tests Checks if value is not a promise. Import /* Node: */ import { isNotPromise } from "froebel/isPromise"; /* Deno: */ import { isNotPromise } from "https://deno.land/x/froebel@v0.20.0/isPromise.ts"; Example (value: number | Promise) => { if (isNotPromise(value)) return value / 2 } Predicate truthy (value: T) => value is PickTruthy ^^source | tests Checks if value is truthy. Literal types are narrowed accordingly. Import /* Node: */ import { truthy } from "froebel/truthy"; /* Deno: */ import { truthy } from "https://deno.land/x/froebel@v0.20.0/truthy.ts"; --------------------------------------------------------------------- falsy (value: T) => value is PickFalsy ^^source | tests Checks if value is falsy. Literal types are narrowed accordingly. Import /* Node: */ import { falsy } from "froebel/truthy"; /* Deno: */ import { falsy } from "https://deno.land/x/froebel@v0.20.0/truthy.ts"; --------------------------------------------------------------------- nullish (value: T) => value is Nullish ^^source | tests Checks if value is nullish. Literal types are narrowed accordingly. Import /* Node: */ import { nullish } from "froebel/nullish"; /* Deno: */ import { nullish } from "https://deno.land/x/froebel@v0.20.0/nullish.ts"; --------------------------------------------------------------------- notNullish (value: null | T) => value is T ^^source | tests Checks if value is not nullish. Literal types are narrowed accordingly. Import /* Node: */ import { notNullish } from "froebel/nullish"; /* Deno: */ import { notNullish } from "https://deno.land/x/froebel@v0.20.0/nullish.ts"; Example const nums = (...values: (number | undefined)[]): number[] => values.filter(notNullish) --------------------------------------------------------------------- isFulfilled (result: PromiseSettledResult) => result is PromiseFulfilledResult ^^source | tests Checks if result (returned from Promise.allSettled) is fulfilled. Import /* Node: */ import { isFulfilled } from "froebel/settled"; /* Deno: */ import { isFulfilled } from "https://deno.land/x/froebel@v0.20.0/settled.ts"; --------------------------------------------------------------------- isRejected (result: PromiseSettledResult) => result is PromiseRejectedResult ^^source | tests Checks if result (returned from Promise.allSettled) is rejected. Import /* Node: */ import { isRejected } from "froebel/settled"; /* Deno: */ import { isRejected } from "https://deno.land/x/froebel@v0.20.0/settled.ts"; String prefix (prefix: T0, str: T1, caseMod?: C) => `${string}` ^^source | tests Returns str prefixed with prefix. Optionally, allows prefxing in camel case, i.e. prefix('foo', 'bar', 'camel') => 'fooBar', or snake case, i.e. prefix('foo', 'bar', 'snake') => 'foo_bar'. The result is strictly typed, so prefix('foo', 'bar') will return the type 'foobar', not just a generic string. Import /* Node: */ import prefix from "froebel/prefix"; /* Deno: */ import prefix from "https://deno.land/x/froebel@v0.20.0/prefix.ts"; --------------------------------------------------------------------- suffix (str: T1, suffix: T0, caseMod?: C) => `${string}` ^^source | tests Returns str suffixed with suffix. Same case and type behavior as prefix. Import /* Node: */ import suffix from "froebel/suffix"; /* Deno: */ import suffix from "https://deno.land/x/froebel@v0.20.0/suffix.ts"; --------------------------------------------------------------------- surround (str: A, surrounding: B) => B extends "" ? A : Surround ^^source | tests Surrounds the str with surrounding. surrounding must have an even length. Import /* Node: */ import { surround } from "froebel/surround"; /* Deno: */ import { surround } from "https://deno.land/x/froebel@v0.20.0/surround.ts"; Example surround("foo", "()") // "(foo)" surround("foo", "({[]})") // "({[foo]})" --------------------------------------------------------------------- capitalize (str: T) => Capitalize ^^source | tests Upper-case first letter of string. Import /* Node: */ import { capitalize } from "froebel/case"; /* Deno: */ import { capitalize } from "https://deno.land/x/froebel@v0.20.0/case.ts"; --------------------------------------------------------------------- uncapitalize (str: T) => Uncapitalize ^^source | tests Lower-case first letter of string Import /* Node: */ import { uncapitalize } from "froebel/case"; /* Deno: */ import { uncapitalize } from "https://deno.land/x/froebel@v0.20.0/case.ts"; --------------------------------------------------------------------- upper (str: T) => Uppercase ^^source | tests Strictly typed String.toUpperCase(). Import /* Node: */ import { upper } from "froebel/case"; /* Deno: */ import { upper } from "https://deno.land/x/froebel@v0.20.0/case.ts"; --------------------------------------------------------------------- lower (str: T) => Lowercase ^^source | tests Strictly typed String.toLowerCase(). Import /* Node: */ import { lower } from "froebel/case"; /* Deno: */ import { lower } from "https://deno.land/x/froebel@v0.20.0/case.ts"; --------------------------------------------------------------------- snake (str: T) => DelimitedCase ^^source | tests Transforms a variable name to snake case. Note: The rules for transforming anything to snake case are somewhat vague. So use this only for very simple names where the resulting value is absolutely unambiguous. For more examples of how names are transformed, have a look at the test cases. Import /* Node: */ import { snake } from "froebel/case"; /* Deno: */ import { snake } from "https://deno.land/x/froebel@v0.20.0/case.ts"; Example snake('fooBar') // 'foo_bar' --------------------------------------------------------------------- kebab (str: T) => DelimitedCase ^^source | tests 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 /* Node: */ import { kebab } from "froebel/case"; /* Deno: */ import { kebab } from "https://deno.land/x/froebel@v0.20.0/case.ts"; Example snake('fooBar') // 'foo-bar' --------------------------------------------------------------------- camel (str: T) => CamelCase ^^source | tests Transforms a variable name to camel case. Note: The rules for transforming anything to camel case are somewhat vague. So use this only for very simple names where the resulting value is absolutely unambiguous. For more examples of how names are transformed, have a look at the test cases. Import /* Node: */ import { camel } from "froebel/case"; /* Deno: */ import { camel } from "https://deno.land/x/froebel@v0.20.0/case.ts"; Example camel('foo_bar') // 'fooBar' --------------------------------------------------------------------- pascal (str: T) => Capitalize ^^source | tests 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 /* Node: */ import { pascal } from "froebel/case"; /* Deno: */ import { pascal } from "https://deno.land/x/froebel@v0.20.0/case.ts"; Example camel('foo_bar') // 'FooBar' --------------------------------------------------------------------- screamingSnake (str: T) => Uppercase ^^source | tests Transforms a variable name to screaming snake case. [see snake] Import /* Node: */ import { screamingSnake } from "froebel/case"; /* Deno: */ import { screamingSnake } from "https://deno.land/x/froebel@v0.20.0/case.ts"; Example snake('fooBar') // 'FOO_BAR' --------------------------------------------------------------------- transformCase (str: T, targetCase: C) => DelimitedCase ^^source | tests Transform a variable name to targetCase [see snake, kebab, camel, pascal, and screamingSnake] Import /* Node: */ import { transformCase } from "froebel/case"; /* Deno: */ import { transformCase } from "https://deno.land/x/froebel@v0.20.0/case.ts"; Math clamp (min: number, num: number, max: number) => number ^^source | tests Clamp num between min and max inclusively. Import /* Node: */ import clamp from "froebel/clamp"; /* Deno: */ import clamp from "https://deno.land/x/froebel@v0.20.0/clamp.ts"; Data Structures BiMap class BiMap(data?: Map | [L, R][], aliasLeft?: AL, aliasRight?: AR) ^^source | tests Bidirectional map. Maps two sets of keys in a one-to-one relation. Both sides are accessible (at .left & .right, or at their respective alias if one was provided in the constructor) with an interface similar to that of the built-in Map and the same iteration behavior. Import /* Node: */ import BiMap from "froebel/bimap"; /* Deno: */ import BiMap from "https://deno.land/x/froebel@v0.20.0/bimap.ts"; Examples const nums = BiMap.from({ one: 1, two: 2 }) // different ways of iterating over the entries [...nums.left] // [['one',1], ['two',2]] [...nums.right] // [[1,'one'], [2,'two']] [...nums.left.keys()] // ['one', 'two'] [...nums.left.values()] // [1, 2] [...nums.right.keys()] // [1, 2] [...nums.right.values()] // ['one', 'two'] [...nums] // [['one',1], ['two',2]] [...nums.right.entries()] // [[1,'one'], [2,'two']] Object.fromEntries(nums.right) // { '1': 'one', '2': 'two' } // setting a value nums.left.three = 3 // when accessing a property using bracket notation (i.e. nums.right[4]), // JavaScript coerces the key to a string, so keys that aren't strings or // symbols must be accessed using the same access methods known from Map. nums.right.set(4, 'four') // remapping values nums.left.tres = 3 // {one: 1, two: 2, tres: 3, four: 4} nums.right.set(4, 'cuatro') // {one: 1, two: 2, tres: 3, cuatro: 4} // deleting delete nums.left.tres // {one: 1, two: 2, cuatro: 4} nums.right.delete(4) // {one: 1, two: 2} // reversing the map const num2Name = nums.reverse() console.log([...num2Name.left]) // [[1,'one'], [2,'two']] console.log(Object.fromEntries(num2Name.right)) // {one: 1, two: 2} // other methods known from built-in Map nums.size // 2 nums.[left|right].size // 2 nums.clear() // equivalent to nums.[left|right].clear() console.log(nums.size) // 0 // giving aliases to both sides const dictionary = new BiMap( [ ['hello', 'hallo'], ['bye', 'tschuss'], ], 'en', 'de' ) dictionary.de.get('hallo') // 'hello' dictionary.en.get('bye') // 'tschuss' delete dictionary.de.hallo console.log(Object.fromEntries(dictionary.en)) // { bye: 'tschuss' } // 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 class SortedArray(compare: Cmp, ...value: T[]) ^^source | tests Sorted array. Behaves much like a regular array but its elements remain sorted using the compare function supplied in the constructor. Contains most of the methods defined on regular JavaScript arrays as long as they don't modify the array's content in place. New elements are added using the add(...values) method. Elements can still be accessed using bracket notation as in plain JavaScript arrays but can't be assigned to using bracket notation (as that could change the element's sort position). Elements can be removed using the delete(...indices) method, which returns an array containing the deleted values. Deleting an element using delete sorted[index] will also work, but results in a TypeScript error because element access is marked readonly. Array methods that pass a reference of the array to a callback (e.g. map, reduce, find) will pass a reference to the SortedArray instance instead. The filter and slice methods will return SortedArray instances instead of plain arrays. Import /* Node: */ import SortedArray from "froebel/sortedArray"; /* Deno: */ import SortedArray from "https://deno.land/x/froebel@v0.20.0/sortedArray.ts"; --------------------------------------------------------------------- SortedMap class SortedMap(compare: Cmp, entries?: null | [K, V][]) ^^source | tests Behaves like a regular JavaScript Map, but its iteration order is dependant on the compare function supplied in the constructor. Note: The item's sort position is only computed automatically on insertion. If you update one of the values that the compare function depends on, you must call the update(key) method afterwards to ensure the map stays sorted. Import /* Node: */ import SortedMap from "froebel/sortedMap"; /* Deno: */ import SortedMap from "https://deno.land/x/froebel@v0.20.0/sortedMap.ts"; About A strictly typed utility library. www.npmjs.com/package/froebel Topics javascript typescript functional-programming utility-library deno Resources Readme License ISC license Stars 491 stars Watchers 8 watching Forks 8 forks Releases 33 tags Used by 2 * @earlofurl @earlofurl / moto-remix * @MathisBullinger @MathisBullinger / ddbjs Contributors 2 * @MathisBullinger MathisBullinger Mathis Bullinger * @AlessandroCipolletti AlessandroCipolletti Alessandro Cipolletti Languages * TypeScript 90.7% * JavaScript 8.4% * Shell 0.9% Footer (c) 2022 GitHub, Inc. Footer navigation * Terms * Privacy * Security * Status * Docs * Contact GitHub * Pricing * API * Training * Blog * About You can't perform that action at this time. You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.