Skip to content

Instantly share code, notes, and snippets.

@kurtlippert
Last active December 15, 2021 14:33
Show Gist options
  • Save kurtlippert/1aff0872e2a215a4662e67eca8a918ac to your computer and use it in GitHub Desktop.
Save kurtlippert/1aff0872e2a215a4662e67eca8a918ac to your computer and use it in GitHub Desktop.
Start to functional standard lib for typescript
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