@beenotung/tslib
Version:
utils library in Typescript
291 lines (290 loc) • 9.18 kB
JavaScript
;
/**
* Created by beenotung on 12/26/16.
* the curried functions are deprecated due to type system limit
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.fmap = exports.concatWithoutDup = exports.doAll = exports.chainFs = exports.composeFs = exports.symbolF = exports.notnot = exports.not = exports.or = exports.and = exports.divMod = exports.quotMod = exports.quot = exports.div = exports.rem = exports.mult = exports.minus = exports.add = exports.defineSymbolF = exports.any = exports.first = exports.lt = exports.gt = exports.neq = exports.eq = exports.none = exports.just = exports.forEach = exports.isStringType = exports.isNumberType = exports.isFunctionType = exports.symbolFs = exports.echoF = exports.apply2 = exports.countWhere = exports.even = exports.odd = exports.compose2 = exports.liftError_noarg = exports.liftError = exports.lift_noarg = exports.lift = exports.flip = exports.compose = exports.filter = exports.length = exports.setProp = exports.deepProp = exports.prop = exports.apply = void 0;
exports.map2 = exports.update = exports.groupByAll = exports.mergeAll = exports.mergeObjs = exports.pushAll = exports.concatAll = exports.concat = exports.foldl1 = exports.foldl = void 0;
exports.iteratorsToArray = iteratorsToArray;
exports.groupBy = groupBy;
const curry_1 = require("./curry");
const map_1 = require("./map");
/** take all args (ignore arity)
* apply :: (..args->a) -> ...args -> a
* */
exports.apply = (0, curry_1.curry)((f) => function () {
return (0, curry_1.id)(f.apply(null, arguments));
});
exports.prop = (0, curry_1.curry)((name, o) => o[name]);
/** cannot represent recursive type, o must eventually contains type A */
exports.deepProp = (0, curry_1.curry)((name, o) => {
if (o[name] !== void 0) {
return o[name];
}
else {
return name.split('.').reduce((acc, c) => acc[c], o);
}
});
/**
* @remark side effect
* @return original object
*
* setProp :: a -> k -> {k:a} -> {k:a}
* */
exports.setProp = (0, curry_1.curry)((a, k, o) => {
o[k] = a;
return o;
});
exports.length = (0, curry_1.curry)((x) => x.length);
exports.filter = (0, curry_1.curry)((f, xs) => xs.filter(f));
exports.compose = (0, curry_1.curry)((f, g, a) => f(g(a)));
/**
* flip :: (a->b) -> (b->a)
* */
exports.flip = (0, curry_1.curry)((f) => (b) => (a) => f(a, b));
/**
* lift :: a -> b -> a
* */
exports.lift = (0, curry_1.curry)((a, _b) => a);
exports.lift_noarg = (0, curry_1.curry)((a) => () => a);
exports.liftError = (0, curry_1.curry)((e, _b) => {
throw e;
});
exports.liftError_noarg = (0, curry_1.curry)((e) => () => {
throw e;
});
exports.compose2 = (0, exports.compose)(exports.compose, exports.compose);
exports.odd = (0, curry_1.curry)((x) => x % 2 === 1);
exports.even = (0, curry_1.curry)((x) => x % 2 === 0);
exports.countWhere = (0, curry_1.curry)((0, exports.compose2)(exports.length, exports.filter));
/**
* @remark side effect
* apply2 :: (a->*) -> (a->b) -> a -> b
* */
exports.apply2 = (0, curry_1.curry)((f, g, x) => {
f(x);
return g(x);
});
/**
* echoF :: (a->*) -> a -> a
* @example echoF (console.log) (1) ~> console.log(1) +> return 1
* */
exports.echoF = (0, exports.flip)(exports.apply2)(curry_1.id);
exports.symbolFs = new Map();
const isFunctionType = (x) => typeof x === 'function';
exports.isFunctionType = isFunctionType;
const isNumberType = (x) => typeof x === 'number';
exports.isNumberType = isNumberType;
const isStringType = (x) => typeof x === 'string';
exports.isStringType = isStringType;
/**
* @remark side effect
*
* forEach :: (a->*) -> [a] -> *
* */
exports.forEach = (0, curry_1.curry)((f, xs) => {
/* xs is ArrayLike, might not has forEach */
const n = xs.length;
for (let i = 0; i < n; i++) {
f(xs[i]);
}
});
/**
* just :: a -> [a]
* */
exports.just = (0, curry_1.curry)((x) => [x]);
/**
* none :: * -> []
* */
exports.none = (0, curry_1.curry)((_x) => []);
exports.eq = (0, curry_1.curry)((a, b) => b === a);
exports.neq = (0, curry_1.curry)((a, b) => b !== a);
exports.gt = (0, curry_1.curry)((a, b) => b > a);
exports.lt = (0, curry_1.curry)((a, b) => b < a);
/**
* first :: (a->Bool) -> [a] -> MaybeSingleton a
* */
exports.first = (0, curry_1.curry)((f, xs) => {
for (const x of xs) {
if (f(x)) {
return (0, exports.just)(x);
}
}
return (0, exports.none)();
});
/**
* any :: (a->Bool) -> [a] -> Bool
* */
exports.any = (0, curry_1.curry)((f, xs) => {
for (const x of xs) {
if (f(x)) {
return true;
}
}
return false;
});
/**
* @remark side effect
* define infix operator (binary function)
* */
exports.defineSymbolF = (0, curry_1.curry)((name, f) => {
exports.symbolFs.set(name, f);
return f;
});
/* number | string atomically for a and b */
exports.add = (0, exports.defineSymbolF)('+', (a, b) => b + a);
/* number | string atomically for a and b */
exports.minus = (0, exports.defineSymbolF)('-', (a, b) => b - a);
exports.mult = (0, exports.defineSymbolF)('*', (a, b) => b * a);
(0, exports.defineSymbolF)('/', (a, b) => b / a);
exports.rem = (0, exports.defineSymbolF)('%', (a, b) => b % a);
exports.div = (0, curry_1.curry)((a, b) => Math.floor(b / a));
exports.quot = (0, curry_1.curry)((a, b) => (b / a) | 0);
/** faster */
exports.quotMod = (0, curry_1.curry)((a, b) => [
(b / a) | 0,
b % a,
]);
/* slower */
exports.divMod = (0, curry_1.curry)((a, b) => {
const d = Math.floor(b / a);
return [d, b - d * a];
});
exports.and = (0, exports.defineSymbolF)('&&', (a, b) => b && a);
exports.or = (0, exports.defineSymbolF)('||', (a, b) => b || a);
exports.not = (0, exports.defineSymbolF)('!', (a) => !a);
exports.notnot = (0, exports.defineSymbolF)('!!', (a) => !!a);
exports.symbolF = (0, curry_1.curry)((name) => exports.symbolFs.get(name) ||
(() => {
throw new Error('symbol ' + name + ' is not defined');
})());
exports.composeFs = (0, curry_1.curry)((fs, acc) => {
for (let i = fs.length - 1; i >= 0; i--) {
acc = fs[i](acc);
}
return acc;
});
exports.chainFs = (0, curry_1.curry)((fs, acc) => {
for (const f of fs) {
acc = f(acc);
}
return acc;
});
/**
* @remark side effect
* f :: unary function <A,B>
* args :: ArrayLike<A>
* */
exports.doAll = (0, curry_1.curry)((f, args) => {
for (const arg of args) {
f(arg);
}
});
/**
* flatten the iterators as a single array
* */
function iteratorsToArray(itrs) {
const xs = [];
for (const itr of itrs) {
xs.push(...Array.from(itr));
}
return xs;
}
exports.concatWithoutDup = (0, curry_1.curry)((as, bs) => {
const acc = new Set();
(0, exports.doAll)((as) => (0, exports.doAll)((a) => acc.add(a), as), [as, bs]);
return iteratorsToArray([acc.values()]);
});
exports.fmap = (0, curry_1.curry)((f, as) => as.map(f));
/**
* groupBy :: (a->k) , [a] -> Map k [a]
* */
function groupBy(f, xs) {
const res = new Map();
for (const x of xs) {
(0, map_1.mapGetOrSetDefault)(res, f(x), () => []).push(x);
}
return res;
}
/**
* foldl :: (b->a->b) -> b -> [a] -> b
* */
exports.foldl = (0, curry_1.curry)((f, acc, xs) => {
for (let i = 0, n = xs.length; i < n; i++) {
acc = f(acc, xs[i]);
}
return acc;
});
exports.foldl1 = (0, curry_1.curry)((f, xs) => {
const n = xs.length;
if (n === 0) {
throw new TypeError('xs should be non-empty ArrayLike<*>');
}
let acc = xs[0];
for (let i = 1; i < n; i++) {
acc = f(acc, xs[i]);
}
return acc;
});
/**
* concat :: [a] -> [a] -> [a]
* */
exports.concat = (0, curry_1.curry)((as, bs) => as.concat(bs));
/**
* concatAll :: [[a]] -> [a]
* */
exports.concatAll = (0, exports.foldl)(exports.concat, []);
/**
* @remark side effect to as
* as -> bs -> __update as__
* */
exports.pushAll = (0, curry_1.curry)((as, bs) => as.push(...bs));
/**
* merge array of plain objects
* do not support merging functions
* do not support instant object (e.g. Map instance)
* merge :: [a|b] -> a & b
* */
exports.mergeObjs = (0, curry_1.curry)((xs) => Object.assign({}, ...xs));
/**
* mergeAll :: (a=>) -> [a] -> [a] -> [a]
* */
exports.mergeAll = (0, curry_1.curry)((f, as, bs) => {
const map = new Map();
for (const xs of [as, bs]) {
for (const x of xs) {
map.set(f(x), x);
}
}
return Array.from(map.values());
});
/**
* groupByAll :: (a->k) -> [[a]] -> Map k [a]
* */
exports.groupByAll = (0, curry_1.curry)((f, xss) => {
const res = new Map();
for (const xs of xss) {
for (const x of xs) {
(0, map_1.mapGetOrSetDefault)(res, f(x), newArray).push(x);
}
}
return res;
});
function newArray() {
return [];
}
/**
* @remark side effect
* update :: (a->__update a__) -> [a] -> [a]
* @return original array
*
* more effective then using map if the original array is going to be discarded anyway
* */
exports.update = (0, curry_1.curry)((f, as) => {
as.forEach(f);
return as;
});
exports.map2 = (0, curry_1.curry)((f, xss) => {
return xss.map(xs => xs.map(f));
});