@beenotung/tslib
Version:
utils library in Typescript
671 lines (670 loc) • 15.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.z_score = exports.mean = exports.range = exports.defaultComparator = void 0;
exports.clearArray = clearArray;
exports.replaceArray = replaceArray;
exports.unique = unique;
exports.rightMost = rightMost;
exports.leftMost = leftMost;
exports.popN = popN;
exports.popUntilN = popUntilN;
exports.shiftN = shiftN;
exports.shiftUntilN = shiftUntilN;
exports.last = last;
exports.maybeLast = maybeLast;
exports.fromFileList = fromFileList;
exports.array_contains = array_contains;
exports.insert = insert;
exports.insert_sorted = insert_sorted;
exports.cloneArray = cloneArray;
exports.sort = sort;
exports.removeByIdx = removeByIdx;
exports.remove = remove;
exports.removeBy = removeBy;
exports.nodup = nodup;
exports.removeDup = removeDup;
exports.insertNoDup = insertNoDup;
exports.insertNoDupWithKey = insertNoDupWithKey;
exports.removeDupByKey = removeDupByKey;
exports.removeByKey = removeByKey;
exports.arrayFromRange = arrayFromRange;
exports.repeat = repeat;
exports.filterByKey = filterByKey;
exports.toArray = toArray;
exports.flatten = flatten;
exports.push = push;
exports.binArray = binArray;
exports.binArrayBy = binArrayBy;
exports.partitionArrayBy = partitionArrayBy;
exports.zipArray = zipArray;
exports.countArray = countArray;
exports.asyncCountArray = asyncCountArray;
exports.max = max;
exports.min = min;
exports.sum = sum;
exports.average = average;
exports.standard_deviation = standard_deviation;
exports.standard_score = standard_score;
exports.sumByFunc = sumByFunc;
exports.maxByFunc = maxByFunc;
exports.minByFunc = minByFunc;
exports.maxByField = maxByField;
exports.minByField = minByField;
exports.sumByField = sumByField;
exports.median = median;
exports.countElement = countElement;
exports.countAll = countAll;
exports.mode = mode;
exports.shuffle = shuffle;
exports.shuffledBinArray = shuffledBinArray;
exports.shuffledIndices = shuffledIndices;
exports.genCombination = genCombination;
exports.flattenAll = flattenAll;
exports.getMaxArraySize = getMaxArraySize;
exports.pushForward = pushForward;
exports.pushBackward = pushBackward;
exports.makeArray = makeArray;
exports.arrayToObject = arrayToObject;
const compare_1 = require("./compare");
const lang_1 = require("./lang");
const map_1 = require("./map");
const maybe_1 = require("./maybe");
const random_1 = require("./random");
const string_1 = require("./string");
/**
* inplace delete all element from the array
* @return old elements
* */
function clearArray(xs) {
return xs.splice(0, xs.length);
}
/**
* inplace replace all element in the array
* @return the old elements
* */
function replaceArray(dest, src) {
clearArray(dest);
dest.push(...src);
return dest;
}
/**
* only use `===` to compare
* @warning slow
* @return new array
* */
function unique(xs) {
return Array.from(new Set(xs));
}
function rightMost(n, xs) {
return xs.slice(xs.length - n, xs.length);
}
function leftMost(n, xs) {
return xs.slice(0, n);
}
/** inplace update */
function popN(n, xs) {
(0, lang_1.forI)(_ => xs.pop(), n);
}
/** inplace update */
function popUntilN(n, xs) {
(0, lang_1.forI)(_ => xs.pop(), xs.length - n);
}
/** inplace update */
function shiftN(n, xs) {
(0, lang_1.forI)(_ => xs.shift(), n);
}
/** inplace update */
function shiftUntilN(n, xs) {
(0, lang_1.forI)(_ => xs.shift(), xs.length - n);
}
function last(xs, skipCheck = false) {
if (!skipCheck && xs.length === 0) {
throw new TypeError('xs is not non-empty array');
}
return xs[xs.length - 1];
}
function maybeLast(xs) {
return maybe_1.Maybe.fromNullable(xs[xs.length - 1]);
}
function fromFileList(files) {
return (0, lang_1.mapI)(i => files.item(i), files.length);
}
function array_contains(xs, x) {
return xs.indexOf(x) !== -1;
}
function insert(xs, index, x) {
xs.splice(index, 0, x);
}
/**
* insert into Ascending sorted array
* */
function insert_sorted(xs, comparator, x, order = 'ascending') {
const target = order === 'ascending' ? compare_1.CompareResult.Larger : compare_1.CompareResult.Smaller;
for (let i = 0; i < xs.length; i++) {
if (comparator(xs[i], x) === target) {
insert(xs, i, x);
return;
}
}
xs.push(x);
}
const defaultComparator = (a, b) => {
if (typeof a === 'string' && typeof b === 'string') {
return (0, string_1.compare_string)(a, b);
}
return (0, compare_1.compare)(a, b);
};
exports.defaultComparator = defaultComparator;
/**
*
* wrapper of slice, because it's confusing between slice and splice
*
* performance reference: https://jsperf.com/array-clone
* */
function cloneArray(xs) {
return xs.slice();
}
/**
* @return in-place sorted, original array
* */
function sort(xs, comparator = exports.defaultComparator) {
return xs.sort(comparator);
}
/**
* @remark inplace update
* @return original array
* */
function removeByIdx(xs, i) {
xs.splice(i, 1);
return xs;
}
/**
* @remark inplace update
* @return original array
* */
function remove(xs, x) {
const idx = xs.indexOf(x);
if (idx !== -1) {
xs.splice(idx, 1);
}
}
/**
* @remark inplace update
* @return original array
* */
function removeBy(xs, f) {
for (let i = 0; i < xs.length; i++) {
if (f(xs[i])) {
xs.splice(i, 1);
return xs;
}
}
return xs;
}
function nodup(xs) {
const s = new Set();
xs.forEach(x => s.add(x));
return Array.from(s.values());
}
/**
* inplace delete all duplicated element from the array (only one copy is kept)
* @return old array
* */
function removeDup(xs) {
const ys = nodup(xs);
clearArray(xs);
xs.push(...ys);
return xs;
}
/**
* inplace insert elements into the array
* @return old array
* */
function insertNoDup(acc, newXs) {
acc.push(...newXs);
return removeDup(acc);
}
/**
* inplace insert elements into the array
* @return old array
* */
function insertNoDupWithKey(acc, newXs, key) {
newXs.forEach(newX => {
if (!acc.find(x => x[key] === newX[key])) {
acc.push(newX);
}
});
return acc;
}
/**
* inplace operation
* @return old array
* */
function removeDupByKey(xs, key) {
const t = {};
xs.map(x => (t[x[key]] = x));
replaceArray(xs, (0, lang_1.objValues)(t));
return xs;
}
/**
* inplace update
* @return old array
* */
function removeByKey(xs, key, keys) {
return replaceArray(xs, xs.filter(x => !array_contains(keys, x[key])));
}
/**
* including end
* */
function arrayFromRange(start, end, step = 1) {
const res = [];
for (let i = start; i <= end; i += step) {
res.push(i);
}
return res;
}
/** @deprecated renamed to arrayFromRange */
exports.range = arrayFromRange;
function repeat(x, n) {
const xs = new Array(n);
xs.fill(x);
return xs;
}
function filterByKey(src, key, keys) {
return src.filter(x => keys.indexOf(x[key]) !== -1);
}
/** @deprecated use Array.from(xs) instead */
function toArray(xs) {
// return mapI(i => xs[i], xs.length);
return Array.prototype.concat.apply([], xs);
}
function flatten(xss) {
return [].concat(...xss);
}
/**
* array.push is not monadic, this is a wrapper to make it monadic
* */
function push(res, ...xs) {
res.push(...xs);
return res;
}
function binArray(xs, binSize) {
const res = [];
let acc = [];
for (const x of xs) {
if (acc.length >= binSize) {
res.push(acc);
acc = [];
}
acc.push(x);
}
if (acc.length !== 0) {
res.push(acc);
}
return res;
}
/**
* non-curry version of `groupBy` in functional.ts
* */
function binArrayBy(xs, mapper) {
const res = new Map();
for (const x of xs) {
const k = mapper(x);
const xs = res.get(k);
if (xs) {
xs.push(x);
}
else {
res.set(k, [x]);
}
}
return res;
}
function partitionArrayBy(xs, f) {
const true_xs = [];
const false_xs = [];
for (const x of xs) {
if (f(x)) {
true_xs.push(x);
}
else {
false_xs.push(x);
}
}
return [true_xs, false_xs];
}
function zipArray(a, b) {
const res = new Array(Math.min(a.length, b.length));
for (let i = 0; i < res.length; i++) {
res[i] = [a[i], b[i]];
}
return res;
}
function countArray(xs, f) {
let acc = 0;
const n = xs.length;
for (let i = 0; i < n; i++) {
if (f(xs[i], i, xs)) {
acc++;
}
}
return acc;
}
function asyncCountArray(xs, f) {
let acc = 0;
return Promise.all(xs.map((x, i, xs) => f(x, i, xs).then(b => (acc += b ? 1 : 0)))).then(() => acc);
}
function max(xs) {
const n = xs.length;
let acc = xs[0];
for (let i = 1; i < n; i++) {
const c = xs[i];
if (acc < c) {
acc = c;
}
}
return acc;
}
function min(xs) {
const n = xs.length;
let acc = xs[0];
for (let i = 1; i < n; i++) {
const c = xs[i];
if (acc > c) {
acc = c;
}
}
return acc;
}
function sum(xs) {
let acc = 0;
const n = xs.length;
for (let i = 0; i < n; i++) {
acc += xs[i];
}
return acc;
}
function average(xs) {
return sum(xs) / xs.length;
}
exports.mean = average;
function standard_deviation(xs, mode = 'sample', mean = average(xs)) {
let acc = 0;
const n = xs.length;
for (let i = 0; i < n; i++) {
const diff = xs[i] - mean;
acc += diff * diff;
}
if (mode === 'sample') {
acc /= n - 1;
}
else {
acc /= n;
}
return Math.sqrt(acc);
}
function standard_score(xs, mode = 'sample') {
const n = xs.length;
const result = new Array(n);
const mean = average(xs);
const sd = standard_deviation(xs, mode, mean);
for (let i = 0; i < n; i++) {
result[i] = (xs[i] - mean) / sd;
}
return result;
}
exports.z_score = standard_score;
function sumByFunc(xs, mapper) {
let acc = 0;
const n = xs.length;
for (let i = 0; i < n; i++) {
acc += mapper(xs[i]);
}
return acc;
}
function maxByFunc(xs, comparator) {
let acc = xs[0];
const n = xs.length;
for (let i = 1; i < n; i++) {
const c = xs[i];
if (comparator(acc, c) < 0) {
acc = c;
}
}
return acc;
}
function minByFunc(xs, comparator) {
let acc = xs[0];
const n = xs.length;
for (let i = 1; i < n; i++) {
const c = xs[i];
if (comparator(acc, c) > 0) {
acc = c;
}
}
return acc;
}
function maxByField(xs, key) {
let acc = xs[0];
const n = xs.length;
for (let i = 1; i < n; i++) {
const c = xs[i];
if ((0, exports.defaultComparator)(acc[key], c[key]) < 0) {
acc = c;
}
}
return acc;
}
function minByField(xs, key) {
let acc = xs[0];
const n = xs.length;
for (let i = 1; i < n; i++) {
const c = xs[i];
if ((0, exports.defaultComparator)(acc[key], c[key]) > 0) {
acc = c;
}
}
return acc;
}
function sumByField(xs, key) {
let acc = 0;
const n = xs.length;
for (let i = 0; i < n; i++) {
acc += xs[i][key];
}
return acc;
}
/**
* side-effect: the array will be sorted in-place if instructed
*
* default will sort the array
* */
function median(xs, options) {
if (xs.length === 0) {
return undefined;
}
if (xs.length === 1) {
return xs[0];
}
if (!options || options.sort !== false) {
xs.sort(options && typeof options.sort === 'function'
? options.sort
: exports.defaultComparator);
}
const n = xs.length;
if (n % 2) {
/* odd number */
return xs[(n - 1) / 2];
}
/* even number */
const m = n / 2;
const a = xs[m - 1];
const b = xs[m];
if (typeof a === 'number' && typeof b === 'number') {
return ((a + b) / 2.0);
}
if (options && options.merger) {
return options.merger(a, b);
}
throw new Error('cannot find median of even array');
}
function countElement(xs, x) {
let acc = 0;
const n = xs.length;
for (let i = 1; i < n; i++) {
if (xs[i] === x) {
acc++;
}
}
return acc;
}
function countAll(xs) {
const counts = new Map();
xs.forEach(x => (0, map_1.incMap)(counts, x));
return counts;
}
function mode(xs) {
const counts = Array.from(countAll(xs).entries());
if (counts.length === 0) {
return undefined;
}
const maxCount = maxByField(counts, 1);
return maxCount ? maxCount[0] : undefined;
}
function shuffle(xs, n = xs.length) {
xs = xs.slice();
for (let i = 0; i < n; i++) {
const a = i;
const b = random_1.Random.nextInt(n);
const t = xs[a];
xs[a] = xs[b];
xs[b] = t;
}
return xs;
}
function shuffledBinArray(xs, binSize, nSwap) {
return binArray(shuffle(xs, nSwap), binSize);
}
function shuffledIndices(n) {
return shuffle((0, exports.range)(0, n - 1));
}
/**
* TODO assign a better name
* e.g. f [a,b,c] 1 ~~> [[a],[b],[c]]
* e.g. f [a,b,c] 2 ~~> [ [a,a],[a,b],[a,c],
* [b,a],[b,b],[b,c],
* [c,a],[c,b],[c,c] ]
* */
function genCombination(cs, size) {
if (size < 1) {
return [];
}
let xss = cs.map(c => [c]);
let i = 1;
for (;;) {
if (i === size) {
return xss;
}
i++;
const acc = [];
const n = xss.length;
for (let x = 0; x < n; x++) {
const n = cs.length;
for (let c = 0; c < n; c++) {
const xs = xss[x].slice();
xs.push(cs[c]);
acc.push(xs);
}
}
xss = acc;
}
}
function flattenAll(xs) {
const ys = [];
flattenIoList(xs, ys);
return ys;
}
function flattenIoList(xs, ys) {
const n = xs.length;
for (let i = 0; i < n; i++) {
const x = xs[i];
if (Array.isArray(x)) {
flattenIoList(x, ys);
}
else {
ys.push(x);
}
}
}
function _getMaxArraySize() {
let i = 1;
try {
for (;;) {
new Array(i);
i += i;
}
}
catch (e) {
let min = i / 2;
let max = i;
for (; min + 1 < max;) {
const m = Math.round((min + max) / 2);
try {
new Array(m);
min = m;
}
catch (e) {
max = m;
}
}
return min;
}
}
let MaxArraySize;
function getMaxArraySize() {
if (!MaxArraySize) {
MaxArraySize = _getMaxArraySize();
}
return MaxArraySize;
}
function pushForward(xs, x) {
const idx = xs.indexOf(x);
if (idx === -1) {
xs.unshift(x);
return;
}
if (idx === 0) {
return; // already at head
}
const t = xs[idx - 1];
xs[idx - 1] = x;
xs[idx] = t;
}
function pushBackward(xs, x) {
const idx = xs.indexOf(x);
if (idx === -1) {
xs.push(x);
return;
}
if (idx === xs.length - 1) {
return; // already at tail
}
const t = xs[idx + 1];
xs[idx + 1] = x;
xs[idx] = t;
}
function makeArray(n, f) {
const res = new Array(n);
for (let i = 0; i < n; i++) {
res[i] = f(i);
}
return res;
}
function arrayToObject(xs, keyFn) {
const o = {};
xs.forEach((x, i, xs) => {
const key = keyFn(x, i, xs);
o[key] = x;
});
return o;
}