Skip to content

Instantly share code, notes, and snippets.

@branneman
Last active August 2, 2022 05:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save branneman/7b9571f53e2c98c2a40f1cd572002f9e to your computer and use it in GitHub Desktop.
Save branneman/7b9571f53e2c98c2a40f1cd572002f9e to your computer and use it in GitHub Desktop.
JavaScript functional programming library
const { curryN, curry } = require('./curry') // https://gist.github.com/branneman/4ffb7ec3fc4a2091849ba5d56742960c
// Typechecks
const isUndef = x => typeof x === 'undefined'
const isNull = x => x === null
const isBool = x => typeof x === 'boolean'
const isNum = x => typeof x === 'number' && isFinite(x)
const isInt = x => typeof x === 'number' && isFinite(x) && Math.floor(x) === x
const isStr = x => typeof x === 'string'
const isArr = x => Array.isArray(x)
const isRegExp = x => isObj(x) && x instanceof RegExp
const isFunc = x => typeof x === 'function'
const isSymbol = x => typeof x === 'symbol'
const isObj = x => x === Object(x) && !isArr(x) && !isFunc(x) && !isSymbol(x)
// Array higher-order
const map = curry((fn, list) => list.map(fn))
const filter = curry((fn, list) => list.filter(fn))
const foldl = curry((fn, init, list) => list.reduce(fn, init))
const foldr = curry((fn, init, list) => list.reduceRight(fn, init))
const find = curry((fn, list) => list.find(fn))
const sort = curry((fn, list) => list.slice().sort(fn))
const includes = curry((fn, list) => list.includes(fn))
// Array
const length = list => list.length
const nth = curry((key, list) => list[key])
const slice = curry((from, to, list) => list.slice(from, to))
const range = curry((from, to) => map(add(from), [...Array(to - from).keys()]))
const head = nth(0)
const init = slice(0, -1)
const tail = slice(1, Infinity)
const last = slice(-1, Infinity)
const concat = curry((a, b) => a.concat(b))
const flatten = foldl(concat, [])
const reverse = list => list.slice().reverse()
const distinct = xs => foldl((acc, curr) => (acc.indexOf(curr) < 0) ? acc.concat([curr]) : acc, [], xs)
const update = curry((key, val, list) => ((list[key] = val), list))
// Logic & Relation
const not = a => !a
const and = curry((a, b) => a && b)
const or = curry((a, b) => a || b)
const eq = curry((left, right) => left === right)
const lt = curry((left, right) => left < right)
const lte = curry((left, right) => left <= right)
const gt = curry((left, right) => left > right)
const gte = curry((left, right) => left >= right)
const min = curry((a, b) => (b < a ? b : a))
const max = curry((a, b) => (b > a ? b : a))
const defaultTo = x => y => isUndef(y) || isNull(y) ? x : y
// Math
const add = curry((a, b) => a + b)
const subtract = curry((a, b) => a - b)
const multiply = curry((a, b) => a * b)
const divide = curry((a, b) => a / b)
const inc = add(1)
const dec = add(-1)
const modulo = curry((a, b) => a % b)
const negate = n => -n
const sum = foldl(add, 0)
const product = foldl(multiply, 1)
const mean = list => sum(list) / length(list)
const median = list =>
length(list) % 2
? list[(length(list) - 1) / 2]
: mean([list[length(list) / 2 - 1], list[length(list) / 2]])
// String
const test = curry((re, str) => re.test(str))
const match = curry((re, str) => str.match(re) || [])
const replace = curry((re, replacement, str) => str.replace(re, replacement))
const lower = s => s.toLowerCase(s)
const upper = s => s.toUpperCase(s)
// Object
const has = curry((key, obj) => Object.prototype.hasOwnProperty.call(obj, key))
const keys = obj => Object.keys(obj)
const values = obj => Object.values(obj)
const entries = obj => Object.entries(obj)
const prop = curry((key, obj) => obj[key])
const assoc = curry((key, val, obj) => ((obj[key] = val), obj))
const pick = curry((keys, obj) => foldl(
(acc, curr) => obj[curr] ? ((acc[curr] = obj[curr]), acc) : acc,
{}, keys))
// Lenses
const lens = curry((get, set) => ({ get, set }))
const view = curry((lens, obj) => lens.get(obj))
const set = curry((lens, val, obj) => lens.set(val, obj))
const over = curry((lens, fn, obj) => set(lens, fn(view(lens, obj)), obj))
const lensIndex = key => lens(nth(key), update(key))
const lensProp = key => lens(prop(key), assoc(key))
// Function
const compose = (...fns) =>
foldl((f, g) => (...args) => f(g(...args)), head(fns), tail(fns))
const pipe = (...fns) => compose(...reverse(fns))
const apply = curry((fn, xs) => fn.apply(null, xs))
const complement = fn => compose(not, fn)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment