Skip to content

Instantly share code, notes, and snippets.

@ntreu14
Created March 6, 2020 16:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ntreu14/8ef61c26790bb018556e6c4538217e0c to your computer and use it in GitHub Desktop.
Save ntreu14/8ef61c26790bb018556e6c4538217e0c to your computer and use it in GitHub Desktop.
Collection of helpful functional JavaScript functions
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