UNPKG

@7urtle/lambda

Version:

Functional programming library in JavaScript.

513 lines (490 loc) 15 kB
import {lengthOf, minusOneToUndefined} from './utils.js'; import {nary} from "./arity.js"; import {upperCaseOf} from "./string.js"; /** * reduce executes input reducer function that over each member of input array [b] to output a single value. It is * the preferred way of working functionally with arrays as it is a pure function that does not cause mutations. * * reduce executes functions in reverse order to reduceRight. * * reduce can be called both as a curried unary function or as a standard ternary function. * * @HindleyMilner reduce :: a -> ((a, b) -> a) -> [b] -> a * * @pure * @param {*} initial * @param {function} reducer * @param {array} list * @return {*} * * @example * import {reduce} from '@7urtle/lambda'; * * const reducer = (accumulator, currentValue) => accumulator + currentValue; * const list = ['a', 'b', 'c']; * * reduce('start')(reducer)(list); // => startabc * * // reduce can be called both as a curried unary function or as a standard ternary function * reduce('start')(reducer)(list) === reduce('start', reducer, list); */ export const reduce = nary(initial => reducer => list => list.reduce(reducer, initial)); /** * reduceRight executes input reducer function that over each member of input array [b] to output a single value. It is * the preferred way of working functionally with arrays as it is a pure function that does not cause mutations. * * reduceRight executes functions in reverse order to reduce. * * reduceRight can be called both as a curried unary function or as a standard ternary function. * * @HindleyMilner reduceRight :: a -> ((a, b) -> a) -> [b] -> a * * @pure * @param {*} initial * @param {function} reducer * @param {array} list * @return {*} * * @example * import {reduceRight} from '@7urtle/lambda'; * * const reducer = (accumulator, currentValue) => accumulator + currentValue; * const list = ['a', 'b', 'c']; * * reduceRight('start')(reducer)(list); // => startcba * * // reduceRight can be called both as a curried unary function or as a standard ternary function * reduceRight('start')(reducer)(list) === reduceRight('start', reducer, list); */ export const reduceRight = nary(initial => reducer => list => list.reduceRight(reducer, initial)); /** * filter executes input checker over each member of input array [a] to filter and output filtered new array [b]. * * If you need to both filter and map over an array, consider using the filterMap function. * * filter can be called both as a curried unary function or as a standard binary function. * * @HindleyMilner filter :: (a -> boolean) -> [a] -> [b] * * @pure * @param {function} checker * @param {array} list * @return {*} * * @example * import {filter} from '@7urtle/lambda'; * * const list = [0, 1, 2, 3] * * filter(a => a > 1)(list); // => [2, 3] * * // filter can be called both as a curried unary function or as a standard binary function * filter(a => a > 1)(list) === filter(a => a > 1, list); */ export const filter = nary(checker => list => list.filter(checker)); /** * filterMap executes mapper function over filtered input array or monad and outputs the resulting array or monad. * * Only one pass through the array is executed unlike the use of map(mapper)(filter(checker)(list)). * * filterMap can be called both as a curried unary function or as a standard ternary function. * * @HindleyMilner filterMap :: (a -> boolean) -> (a -> b) -> [a] -> [b] * * @pure * @param {function} checker * @param {function} mapper * @param {array} list * @return {*} * * @example * import {filterMap} from '@7urtle/lambda'; * * const list = [0, 1, 2, 3] * const mapper = a => a + 1; * const checker = a => a > 1; * * filterMap(checker)(mapper)(list); // => [3, 4] * filterMap(a => a > 1)(a => a + 1)([0, 1, 2, 3]); // => [3, 4] * * const mapOverLargerThanOne = filterMap(checker); * mapOverLargerThanOne(mapper)(list); // => [3, 4] * * // filterMap can be called both as a curried unary function or as a standard ternary function * filterMap(a => a > 1)(a => a + 1)(list) === filterMap(a => a > 1, a => a + 1, list); */ export const filterMap = nary(checker => mapper => list => reduce([])((acc, current) => checker(current) ? acc.push(mapper(current)) && acc : acc)(list)); /** * find executes input checker over each member of input array [a] and outputs the first array member that matches checker or undefined. * * find can be called both as a curried unary function or as a standard binary function. * * @HindleyMilner find :: (a -> boolean) -> [a] -> [b] * * @pure * @param {function} checker * @param {array} list * @return {*} * * @example * import {find} from '@7urtle/lambda'; * * const list = [0, 1, 2, 3] * * find(a => a > 1)(list); // => 2 * find(a => a > 3)(list); // => undefined * * // find can be called both as a curried unary function or as a standard binary function * find(a => a > 1)(list) === find(a => a > 1, list); */ export const find = nary(checker => list => list.find(checker)); /** * findIndex executes input checker over each member of input array [a] and outputs the index of first array member that matches checker or undefined. * * findIndex can be called both as a curried unary function or as a standard binary function. * * @HindleyMilner findIndex :: (a -> boolean) -> [a] -> [b] * * @pure * @param {function} checker * @param {array} list * @return {*} * * @example * import {findIndex} from '@7urtle/lambda'; * * const list = [2, 3, 4]; * * findIndex(a => a > 2)(list); // => 1 * findIndex(a => a > 4)(list); // => undefined * * // findIndex can be called both as a curried unary function or as a standard binary function * findIndex(a => a > 1)(list) === findIndex(a => a > 1, list); */ export const findIndex = nary(checker => list => minusOneToUndefined(list.findIndex(checker))); /** * join outputs a string created by joining input array members with input separator. * * join can be called both as a curried unary function or as a standard binary function. * * @HindleyMilner join :: string -> [a] -> string * * @pure * @param {string} separator * @param {array} list * @return {*} * * @example * import {join} from '@7urtle/lambda'; * * const list = [2, 3, 4]; * * join('')(list); // => '234' * join(' and ')(list); // => '2 and 3 and 4' * join()(list); // => '2,3,4' * * // join can be called both as a curried unary function or as a standard binary function * join('')(list) === join('', list); */ export const join = nary(separator => list => list.join(separator)); /** * keysOf outputs array of string keys of input array or object. * * @HindleyMilner keysOf :: object -> [string] * * @pure * @param {Object|array} list * @return {array} * * @example * import {keysOf} from '@7urtle/lambda'; * * keysOf([2, 3, 4]); // => ['0', '1', '2'] * keysOf({1: 2, 2: 3}); // => ['1', '2'] */ export const keysOf = Object.keys; /** * entriesOf outputs array of arrays of string keys and raw values of input array or object. * * @HindleyMilner entriesOf :: object -> [[string, a]] * * @pure * @param {Object|array} list * @return {array} * * @example * import {entriesOf} from '@7urtle/lambda'; * * entriesOf([2, 3, 4]); // => [['0', 2], ['1', 3], ['2', 4]] * entriesOf({1: 2, 2: 3}); // => [['1', 2],['2', 3]] */ export const entriesOf = Object.entries; /** * everyOf outputs true if every element of input array passes input checker function as true. * * everyOf can be called both as a curried unary function or as a standard binary function. * * @HindleyMilner everyOf :: (a -> boolean) -> [a] -> boolean * * @pure * @param {function} checker * @param {array} list * @return {boolean} * * @example * import {everyOf} from '@7urtle/lambda'; * * everyOf(a => a > 1)([2, 3, 4]); // => true * everyOf(a => a > 5)([2, 3, 4]); // => false * * // everyOf can be called both as a curried unary function or as a standard binary function * everyOf(a => a > 1)([2, 3, 4]) === everyOf(a => a > 1, [2, 3, 4]); */ export const everyOf = nary(checker => list => list.every(checker)); /** * slice outputs selected array elements as an array based on input range. First argument end * represents the ending index (not length) and start represents the starting index in the input * array list. * * slice can be called both as a curried unary function or as a standard ternary function. * * @HindleyMilner slice :: number -> number -> [a] -> [a] * * @pure * @param {number} end * @param {number} start * @param {array} list * @return {array} * * @example * import {slice} from '@7urtle/lambda'; * * slice(2)(1)([1, 2, 3, 4, 5]); // => [2] * slice(2)(0)([1, 2, 3, 4, 5]); // => [1, 2] * slice(8)(7)([1, 2, 3, 4, 5]); // => [] * * // slice can be called both as a curried unary function or as a standard ternary function * slice(2)(1)([1, 2, 3, 4, 5]) === slice(2, 1, [1, 2, 3, 4, 5]); */ export const slice = nary(end => start => list => list.slice(start, end)); /** * some outputs true if any element of input array passes input checker function as true. * * some can be called both as a curried unary function or as a standard binary function. * * @HindleyMilner some :: (a -> boolean) -> [a] -> boolean * * @pure * @param {function} checker * @param {array} list * @return {boolean} * * @example * import {some} from '@7urtle/lambda'; * * someOf(a => a > 1)([2, 3, 4]); // => true * someOf(a => a > 5)([2, 3, 4]); // => false * * // some can be called both as a curried unary function or as a standard binary function * someOf(a => a > 1)([2, 3, 4]) === someOf(a => a > 1, [2, 3, 4]); */ export const someOf = nary(checker => list => list.some(checker)); /** * sort outputs an array sorted based on input compare function. * * sort can be called both as a curried unary function or as a standard binary function. * * @HindleyMilner sort :: (a -> number) -> [a] -> [a] * * @pure * @param {function} compare * @param {array} list * @return {array} * * @example * import {sort} from '@7urtle/lambda'; * * sort((a, b) => a < b ? -1 : a > b ? 1 : 0)(['a', 'd', 'c', 'd']); // => ['a', 'c', 'd', 'd'] * sort((a, b) => a - b)([5, 3, 6]); // => [3, 5, 6] * * // sort can be called both as a curried unary function or as a standard binary function * sort((a, b) => a - b)([5, 3, 6]) === sort((a, b) => a - b, [5, 3, 6]); */ export const sort = nary(compare => list => [...list].sort(compare)); /** * sortAlphabetically outputs an array sorted alphabetically from a to z. * * @HindleyMilner sortAlphabetically :: [string] -> [string] * * @pure * @param {array} list * @return {array} * * @example * import {sortAlphabetically} from '@7urtle/lambda'; * * sortAlphabetically(['petra', 'Martin', 'Petra']); // => ['Martin', 'petra', 'Petra'] */ export const sortAlphabetically = sort((a, b) => (a => b => a < b ? -1 : a > b ? 1 : 0)(upperCaseOf(a))(upperCaseOf(b))); /** * sortAlphabeticallyZA outputs an array sorted alphabetically from z to a. * * @HindleyMilner sortAlphabeticallyZA :: [string] -> [string] * * @pure * @param {array} list * @return {array} * * @example * import {sortAlphabeticallyZA} from '@7urtle/lambda'; * * sortAlphabeticallyZA(['petra', 'Martin', 'Petra']); // => ['petra', 'Petra', 'Martin'] */ export const sortAlphabeticallyZA = sort((a, b) => (a => b => a < b ? 1 : a > b ? -1 : 0)(upperCaseOf(a))(upperCaseOf(b))); /** * sortNumerically outputs an array sorted numerically from 1 to 2. * * @HindleyMilner sortNumerically :: [number] -> [number] * * @pure * @param {array} list * @return {array} * * @example * import {sortNumerically} from '@7urtle/lambda'; * * sortNumerically([3, 4, 1, 3]); // => [1, 3, 3, 4] */ export const sortNumerically = sort((a, b) => a - b); /** * sortNumerically21 outputs an array sorted numerically from 2 to 1. * * @HindleyMilner sortNumerically21 :: [number] -> [number] * * @pure * @param {array} list * @return {array} * * @example * import {sortNumerically21} from '@7urtle/lambda'; * * sortNumerically21([3, 4, 1, 3]); // => [4, 3, 3, 1] */ export const sortNumerically21 = sort((a, b) => b - a); /** * headOf outputs the first item (head) from the input array. * * @HindleyMilner headOf :: [a] -> a * * @pure * @param {array} list * @return {any} * * @example * import {headOf} from '@7urtle/lambda'; * * headOf([3, 4, 1, 8]); // => 3 * headOf([8]); // => 8 * headOf([]); // => undefined */ export const headOf = list => list[0]; /** * tailOf outputs the the input array without its first item. * * @HindleyMilner tailOf :: [a] -> [] * * @pure * @param {array} list * @return {array} * * @example * import {tailOf} from '@7urtle/lambda'; * * tailOf([3, 4, 1, 8]); // => [4, 1, 8] * tailOf([8]); // => [] * tailOf([]); // => [] */ export const tailOf = list => list.slice(1); /** * initOf outputs the the input array without its last item. * * @HindleyMilner initOf :: [a] -> [] * * @pure * @param {array} list * @return {array} * * @example * import {initOf} from '@7urtle/lambda'; * * initOf([3, 4, 1, 8]); // => [3, 4, 1] * initOf([8]); // => [] * initOf([]); // => [] */ export const initOf = list => slice(lengthOf(list) -1)(0)(list); /** * lastOf outputs the last item from the input array. * * @HindleyMilner lastOf :: [a] -> a * * @pure * @param {array} list * @return {any} * * @example * import {lastOf} from '@7urtle/lambda'; * * lastOf([3, 4, 1, 8]); // => 8 * lastOf([3]); // => 3 * lastOf([]); // => undefined */ export const lastOf = list => list[lengthOf(list) -1]; /** * groupBy outputs an objects with groups produced by an input function over input list. * * groupBy can be called both as a curried unary function or as a standard binary function. * * @HindleyMilner groupBy :: (a -> b) -> [a] -> {b: a} * * @pure * @param {function} fn * @param {array} list * @return {object} * * @example * import {groupBy} from '@7urtle/lambda'; * * groupBy(a => a.length)(['one', 'two', 'three']); * // => {"3": ["one", "two"], "5": ["three"]} * * groupBy(a => a % 2)([1, 2, 3]); * // => {"0": [2], "1": [1, 3]} * * // groupBy can be called both as a curried unary function or as a standard binary function * groupBy(a => a.length)(['one', 'two', 'three']) === groupBy(a => a.length, ['one', 'two', 'three']) */ export const groupBy = nary(fn => list => reduce ({}) ((acc, current) => (acc[fn(current)] = acc[fn(current)] || []).push(current) && acc ) (list)); /** * randomOf outputs a random item from the input array. * * @HindleyMilner randomOf :: [a] -> a * * @pure * @param {array} list * @return {any} * * @example * import {randomOf} from '@7urtle/lambda'; * * randomOf([3, 4, 1, 8]); // => 8 * randomOf([3]); // => 3 * randomOf([]); // => undefined */ export const randomOf = list => list[Math.floor(Math.random() * lengthOf(list))];