Created
March 6, 2020 16:06
-
-
Save ntreu14/8ef61c26790bb018556e6c4538217e0c to your computer and use it in GitHub Desktop.
Collection of helpful functional JavaScript functions
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
function () { | |
var concatOptionals; | |
this.set = function (obj, property) { | |
return function (value) { | |
obj[property] = value; | |
return obj; | |
}; | |
}; | |
this.setProp = function (property, value) { | |
return function (obj) { | |
obj[property] = value; | |
return obj; | |
}; | |
}; | |
this.delaySet = function (obj, property, value) { | |
return function () { | |
obj[property] = value; | |
return obj; | |
}; | |
}; | |
this.prop = function (property) { | |
return function (object) { | |
return object[property]; | |
}; | |
}; | |
this.propOf = flip(this.prop); | |
this.flip = flip; | |
this.indexIn = function (array) { | |
return function (value) { | |
return array.indexOf(value); | |
}; | |
}; | |
this.propEq = function (property, value) { | |
return function (object) { | |
return object[property] === value; | |
}; | |
}; | |
this.propMap = function (property, f) { | |
return function (obj) { | |
var result = {}; | |
for (var key in obj) { | |
result[key] = obj[key]; | |
} | |
result[property] = f(result); | |
return result; | |
}; | |
}; | |
this.propOr = function (value, property) { | |
return function (object) { | |
return typeof object === 'object' && property in object ? object[property] : value; | |
}; | |
}; | |
this.has = function (property) { | |
return function (object) { | |
return property in object; | |
}; | |
}; | |
this.negate = function (predicate) { | |
return function (x) { | |
return !predicate(x); | |
}; | |
}; | |
this.not = function (v) { | |
return !v; | |
}; | |
this.find = function (predicate) { | |
return function (values) { | |
return values.filter(predicate)[0]; | |
}; | |
}; | |
this.filter = function (predicate) { | |
return function (values) { | |
return values.filter(predicate); | |
}; | |
}; | |
this.map = function (f) { | |
return function (functor) { | |
return functor.map(f); | |
}; | |
}; | |
this.zip = function (as) { | |
return function (bs) { | |
return concatOptionals(as | |
.map(function (fst, i) { | |
return i in bs ? | |
new Some({ fst: fst, snd: bs[i] }) : | |
new None(); | |
})); | |
}; | |
}; | |
function take(n) { | |
return function (array) { | |
return array.slice(0, n); | |
}; | |
} | |
this.take = take; | |
function drop(n) { | |
return function (array) { | |
return array.slice(n); | |
}; | |
} | |
this.drop = drop; | |
function chunkToSize(n) { | |
return function (array) { | |
if (array.length === 0) { | |
return []; | |
} | |
return [take(n)(array)].concat(chunkToSize(n)(drop(n)(array))); | |
}; | |
} | |
this.chunkToSize = chunkToSize; | |
this.collect = function (f) { | |
return compose2(this.concat, this.map(f)); | |
}; | |
this.equal = function (a) { | |
return function (b) { | |
return a === b; | |
}; | |
}; | |
this.isEmptyArray = pipe2(this.prop('length'), this.equal(0)); | |
this.matchBy = function (f) { | |
return function (cases) { | |
return function (v) { | |
return (cases[f(v)] || cases['otherwise'])(v); | |
}; | |
}; | |
}; | |
this.defaultTo = function (def) { | |
return function (value) { | |
return value === undefined ? def : value; | |
}; | |
}; | |
this.foldr = function (accumulator, zero) { | |
return function (values) { | |
return values.reduce(function (acc, curr) { return accumulator(curr, acc); }, zero); | |
}; | |
}; | |
this.allSame = function (values) { | |
return values.every(this.equal(values[0])); | |
}; | |
this.concat = function (arrayOfArrays) { | |
return arrayOfArrays.reduce(function (acc, array) { return acc.concat(array); }, []); | |
}; | |
this.sumBy = function (transform) { | |
return function (values) { | |
return values.reduce(function (acc, value) { return acc + transform(value); }, 0); | |
}; | |
}; | |
this.sum = this.sumBy(id); | |
this.distinctBy = function (f) { | |
return this.filter(function (value, index, array) { | |
return array.map(f).indexOf(f(value)) === index; | |
}); | |
}; | |
this.distinct = this.distinctBy(id); | |
this.maxOr = function (aDefault) { | |
return function (values) { | |
if (values.length === 0) { | |
return aDefault; | |
} else { | |
return values.slice(1).reduce(max, values[0]); | |
} | |
}; | |
}; | |
this.ifElse = function (predicate, consequentF, alternativeF) { | |
return function (value) { | |
return predicate(value) ? consequentF(value) : alternativeF(value); | |
}; | |
}; | |
this.whenElse = function (predicate, consequent, alternative) { | |
return this.ifElse(predicate, always(consequent), always(alternative)); | |
}; | |
this.when = function (predicate, consequentF) { | |
return this.ifElse(predicate, consequentF, id); | |
}; | |
this.ifTruthy = function (consequentF, alternativeF) { | |
return this.ifElse(Boolean, consequentF, alternativeF); | |
}; | |
this.ifFalsy = function (consequentF, alternativeF) { | |
return this.ifTruthy(alternativeF, consequentF); | |
}; | |
this.both = function (predicateA, predicateB) { | |
return function (value) { | |
return Boolean(predicateA(value) && predicateB(value)); | |
}; | |
}; | |
this.either = function (predicateA, predicateB) { | |
return function (value) { | |
return Boolean(predicateA(value) || predicateB(value)); | |
}; | |
}; | |
this.id = id; | |
this.always = always; | |
this.compose = function (functions) { | |
return functions.reduce(compose2, this.id); | |
}; | |
this.pipe = function (functions) { | |
return functions.reduce(pipe2, this.id); | |
}; | |
this.invoker = function (args) { | |
return function () { | |
return args[0].apply(null, args.slice(1)); | |
}; | |
}; | |
this.caller = function (method, args) { | |
return function (object) { | |
return object[method].apply(null, args); | |
}; | |
}; | |
this.allPass = function (conditions) { | |
return function (value) { | |
return conditions.reduce(function (result, predicate) { | |
return result && predicate(value) | |
}, true); | |
}; | |
}; | |
this.some = function (value) { | |
return new Some(value); | |
}; | |
this.none = function () { | |
return new None(); | |
}; | |
this.optionUnlessSentinel = function (sentinel) { | |
return this.ifElse(this.equal(sentinel), this.none, this.some); | |
}; | |
this.optionFromUndefinable = this.optionUnlessSentinel(undefined); | |
this.joinStrings = function (sep) { | |
return function (values) { | |
return values.join(sep); | |
}; | |
}; | |
concatOptionals = this.collect(this.caller('toArray', [])) | |
this.concatOptionals = concatOptionals; | |
this.smushOptionals = function (keysAndOptions) { | |
function aux(values, acc) { | |
if (values.length < 2) { | |
return acc; | |
} | |
return aux( | |
values.slice(2), | |
acc.bind(function (obj) { | |
return values[1].map(function (value) { | |
obj[values[0]] = value; | |
return obj; | |
}); | |
}) | |
); | |
} | |
return aux(keysAndOptions, new Some({})); | |
}; | |
this.nth = function (n) { | |
return this.ifElse( | |
this.has(n), | |
compose2(this.some, this.prop(n)), | |
this.none | |
); | |
}; | |
this.head = this.nth(0); | |
this.last = | |
pipe2( | |
this.ifElse( | |
this.isEmptyArray, | |
this.none, | |
this.some | |
), | |
this.map(this.foldr(id, 'should not matter')) | |
); | |
this.includesExactly = function (value) { | |
return function (array) { | |
return array.indexOf(value) !== -1; | |
}; | |
}; | |
this.includedIn = function (values) { | |
return function (value) { | |
return values.indexOf(value) !== -1; | |
}; | |
}; | |
this.toPairs = function (object) { | |
var key, pairs = [] | |
for (key in object) { | |
pairs.push({ fst: key, snd: object[key] }); | |
} | |
return pairs; | |
}; | |
this.objectValues = this.pipe([this.toPairs, this.map(this.prop('snd'))]); | |
this.promiseThen = function (success, error, notify) { | |
return function (promise) { | |
return promise.then(success, error, notify); | |
}; | |
}; | |
}); | |
function Some(value) { | |
this.isSome = true; | |
this.isNone = false; | |
this.valueOr = always(value); | |
this.map = function (f) { | |
return new Some(f(value)); | |
}; | |
this.alt = function (_) { | |
return new Some(value); | |
}; | |
this.toArray = function () { | |
return [value]; | |
}; | |
this.match = function (onSome, _) { | |
return onSome(value); | |
}; | |
this.bind = function (f) { | |
return f(value); | |
}; | |
this.forEach = function (callback) { | |
callback(value); | |
}; | |
} | |
function None() { | |
this.isSome = false; | |
this.isNone = true; | |
this.valueOr = id; | |
this.alt = function (other) { | |
return other; | |
}; | |
this.map = function (_) { | |
return new None(); | |
}; | |
this.toArray = function () { | |
return []; | |
}; | |
this.match = function (_, onNone) { | |
return onNone(); | |
}; | |
this.bind = function (_) { | |
return new None(); | |
}; | |
this.forEach = function (_) {}; | |
} | |
function always(a) { | |
return function () { | |
return a; | |
}; | |
} | |
function id(x) { | |
return x; | |
} | |
function flip(f) { | |
return function (x) { | |
return function (y) { | |
return f(y)(x); | |
}; | |
}; | |
} | |
function compose2(f, g) { | |
return function (x) { | |
return f(g(x)); | |
}; | |
} | |
function pipe2(f, g) { | |
return function (x) { | |
return g(f(x)); | |
}; | |
} | |
function max(a, b) { return Math.max(a, b); } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment