Last active
December 15, 2021 14:33
-
-
Save kurtlippert/1aff0872e2a215a4662e67eca8a918ac to your computer and use it in GitHub Desktop.
Start to functional standard lib for typescript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Nothing, Just } from "https://jspm.dev/purify-ts/Maybe" | |
import { List } from "https://jspm.dev/purify-ts/List" | |
import { Tuple } from "https://jspm.dev/purify-ts/Tuple" | |
import { pipe } from "https://jspm.dev/fp-ts/pipeable" | |
// === String Helpers === | |
// to replace those found w/ `lodash` that don't really support `Maybe` | |
// inspired by https://github.com/NoRedInk/haskell-libraries/blob/trunk/nri-prelude/src/Text.hs | |
export const String = { | |
/** | |
* Determine if a string is empty. | |
* | |
* @example | |
* isEmpty("") === true | |
* isEmpty("the world") === false | |
*/ | |
isEmpty: (str: string) => str.length === 0, | |
/** | |
* Get the length of a string. | |
* | |
* @example | |
* length("innumerable") === 11 | |
* length("") === 0 | |
*/ | |
length: (str: string) => str.length, | |
/** | |
* Reverse a string. | |
* | |
* @example | |
* reverse("stressed") === "desserts" | |
*/ | |
reverse: (str: string) => | |
str.split("").reduce((acc: string[], cur: string) => [cur, ...acc], []).join(""), | |
/** | |
* Repeat a string /n/ times. | |
* | |
* @example | |
* repeat(3)("ha") === "hahaha" | |
* const repeatThree = repeat(3) | |
* repeatThree("ha") === "hahaha" | |
*/ | |
repeat: (thisMany: number) => (str: string) => str.repeat(thisMany), | |
// === BUILDING AND SPLITTING === | |
/** | |
* Append two strings. | |
* | |
* @example | |
* append("butter")("fly") === "butterfly" | |
* const prependButter = append("butter") | |
* prependButter("fly") === "butterfly" | |
*/ | |
append: (leftStr: string) => (rightStr: string) => leftStr + rightStr, | |
/** | |
* Concatenate may strings into one. | |
* | |
* @example | |
* concat(["never", "the", "less"]) === "nevertheless" | |
*/ | |
concat: (strList: string[]) => strList.join(""), | |
/** | |
* Split a string using a given separator. | |
* | |
* @example | |
* split(",")("cat,dog,cow") === ["cat", "dog", "cow"] | |
* | |
* const splitComma = split(",") | |
* splitComma("cat,dog,cow") === ["cat", "dog", "cow"] | |
*/ | |
split: (separator: string) => (str: string) => str.split(separator), | |
/** | |
* Put many strings together with a given separator | |
* | |
* @example | |
* join("a")(["H", "w", "ii", "n"]) === "Hawaiian" | |
* join(" ")("cat", "dog", "cow") === "cat dog cow" | |
* join("/")(["home", "kurt", "Desktop"] === "home/kurt/Desktop") | |
* | |
* const joinSlash = join("/") | |
* joinSlash(["home", "kurt", "Desktop"]) === "home/kurt/Desktop" | |
*/ | |
join: (separator: string) => (strList: string[]) => strList.join(separator), | |
/** | |
* Break a string into words, splitting on chunks of whitespace. | |
* | |
* @example | |
* words("How are \t you? \n Good?") === ["How", "are", "you?", "Good?"] | |
*/ | |
words: (str: string) => str.split(/\s/g).filter(s => s.length > 0), | |
/** | |
* Break a string into lines, splitting on newlines. | |
* | |
* @example | |
* lines("How are you?\nGood?") === ["How are you?", "Good?"] | |
*/ | |
lines: (str: string) => str.split(/\n/g), | |
/** | |
* Take a substring given a start and end index. | |
* Negative indexes are taken starting from the /end/ of the list. | |
* | |
* @example | |
* slice(7)(9)("snakes on a plane!") === "on" | |
* slice(0)(6)("snakes on a plane!") === "snakes" | |
* slice(0)(-7)("snakes on a plane!") === "snakes on a" | |
* slice(-6)(-1)("snakes on a plane!") === "plane" | |
*/ | |
slice: (startIndex: number) => (endIndex: number) => (str: string) => str.slice(startIndex, endIndex), | |
/** | |
* Take /n/ characters from the left side of a string. | |
* | |
* @example | |
* left(2)("Mulder") === "Mu" | |
*/ | |
left: (take: number) => (str: string) => str.slice(0, take), | |
/** | |
* Take /n/ characters from the right side of a string. | |
* | |
* @example | |
* right(2)("Scully") === "ly" | |
*/ | |
right: (take: number) => (str: string) => str.slice(-take), | |
/** | |
* Drop /n/ characters from the left side of a string. | |
* | |
* @example | |
* dropLeft(2)("The Lone Gunmen") === "e Lone Gunmen" | |
*/ | |
dropLeft: (drop: number) => (str: string) => str.slice(drop), | |
/** | |
* Drop /n/ characters from the left side of a string. | |
* | |
* @example | |
* dropRight(2)("Cigarette Smoking Man") === "Cigarette Smoking M" | |
*/ | |
dropRight: (drop: number) => (str: string) => str.slice(0, -drop), | |
pad: (thisMany: number) => (padding: string) => (str: string) => { | |
const pad = padding.repeat(thisMany) | |
return pad + str + pad | |
}, | |
padLeft: (thisMany: number) => (padding: string) => (str: string) => { | |
const pad = padding.repeat(thisMany) | |
return pad + str | |
}, | |
padRight: (thisMany: number) => (padding: string) => (str: string) => { | |
const pad = padding.repeat(thisMany) | |
return str + pad | |
}, | |
isNumber: (str: string) => | |
Object.is(parseFloat(str), NaN) | |
? false | |
: true, | |
fromNumber: (num: number) => num.toString(), | |
toNumber: (str: string) => { | |
const parsedFloat = parseFloat(str) | |
return Object.is(parsedFloat, NaN) | |
? Nothing | |
: Just(parsedFloat) | |
}, | |
toList: (str: string) => str.split(""), | |
fromList: (strList: string[]) => strList.join(""), | |
cons: (toPrepend: string) => (str: string) => toPrepend + str, | |
uncons: (str: string) => | |
str.slice(0, 1) === "" | |
? Nothing | |
: Just(Tuple(str.slice(0, 1), str.slice(1, str.length))), | |
map: (tranformFunc: (char: string) => string) => (str: string) => | |
str.split("").map(tranformFunc).join(""), | |
filter: (testFunc: (char: string) => boolean) => (str: string) => | |
str.split("").filter(testFunc).join(""), | |
foldl: <T>(foldFn: (p1: string) => (p2: T) => T) => (accumulator: T) => (str: string): T => { | |
const chars = str.split("") | |
for (let i of chars) { | |
accumulator = foldFn(i)(accumulator) | |
} | |
return accumulator | |
}, | |
foldr: <T>(foldFn: (p1: string) => (p2: T) => T) => (accumulator: T) => (str: string): T => { | |
const chars = str.split("") | |
for (let i = chars.length - 1; i >= 0; --i) { | |
accumulator = foldFn(List.at(i, chars).orDefault(""))(accumulator) | |
} | |
return accumulator | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment