257 lines
6.9 KiB
JavaScript
Executable File
257 lines
6.9 KiB
JavaScript
Executable File
/**
|
|
* Returns the last element of an array.
|
|
* @param array The array.
|
|
* @param n Which element from the end (default is zero).
|
|
*/
|
|
export function tail(array, n) {
|
|
if (n === void 0) { n = 0; }
|
|
return array[array.length - (1 + n)];
|
|
}
|
|
export function tail2(arr) {
|
|
if (arr.length === 0) {
|
|
throw new Error('Invalid tail call');
|
|
}
|
|
return [arr.slice(0, arr.length - 1), arr[arr.length - 1]];
|
|
}
|
|
export function equals(one, other, itemEquals) {
|
|
if (itemEquals === void 0) { itemEquals = function (a, b) { return a === b; }; }
|
|
if (one === other) {
|
|
return true;
|
|
}
|
|
if (!one || !other) {
|
|
return false;
|
|
}
|
|
if (one.length !== other.length) {
|
|
return false;
|
|
}
|
|
for (var i = 0, len = one.length; i < len; i++) {
|
|
if (!itemEquals(one[i], other[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
export function binarySearch(array, key, comparator) {
|
|
var low = 0, high = array.length - 1;
|
|
while (low <= high) {
|
|
var mid = ((low + high) / 2) | 0;
|
|
var comp = comparator(array[mid], key);
|
|
if (comp < 0) {
|
|
low = mid + 1;
|
|
}
|
|
else if (comp > 0) {
|
|
high = mid - 1;
|
|
}
|
|
else {
|
|
return mid;
|
|
}
|
|
}
|
|
return -(low + 1);
|
|
}
|
|
/**
|
|
* Takes a sorted array and a function p. The array is sorted in such a way that all elements where p(x) is false
|
|
* are located before all elements where p(x) is true.
|
|
* @returns the least x for which p(x) is true or array.length if no element fullfills the given function.
|
|
*/
|
|
export function findFirstInSorted(array, p) {
|
|
var low = 0, high = array.length;
|
|
if (high === 0) {
|
|
return 0; // no children
|
|
}
|
|
while (low < high) {
|
|
var mid = Math.floor((low + high) / 2);
|
|
if (p(array[mid])) {
|
|
high = mid;
|
|
}
|
|
else {
|
|
low = mid + 1;
|
|
}
|
|
}
|
|
return low;
|
|
}
|
|
/**
|
|
* Like `Array#sort` but always stable. Usually runs a little slower `than Array#sort`
|
|
* so only use this when actually needing stable sort.
|
|
*/
|
|
export function mergeSort(data, compare) {
|
|
_sort(data, compare, 0, data.length - 1, []);
|
|
return data;
|
|
}
|
|
function _merge(a, compare, lo, mid, hi, aux) {
|
|
var leftIdx = lo, rightIdx = mid + 1;
|
|
for (var i = lo; i <= hi; i++) {
|
|
aux[i] = a[i];
|
|
}
|
|
for (var i = lo; i <= hi; i++) {
|
|
if (leftIdx > mid) {
|
|
// left side consumed
|
|
a[i] = aux[rightIdx++];
|
|
}
|
|
else if (rightIdx > hi) {
|
|
// right side consumed
|
|
a[i] = aux[leftIdx++];
|
|
}
|
|
else if (compare(aux[rightIdx], aux[leftIdx]) < 0) {
|
|
// right element is less -> comes first
|
|
a[i] = aux[rightIdx++];
|
|
}
|
|
else {
|
|
// left element comes first (less or equal)
|
|
a[i] = aux[leftIdx++];
|
|
}
|
|
}
|
|
}
|
|
function _sort(a, compare, lo, hi, aux) {
|
|
if (hi <= lo) {
|
|
return;
|
|
}
|
|
var mid = lo + ((hi - lo) / 2) | 0;
|
|
_sort(a, compare, lo, mid, aux);
|
|
_sort(a, compare, mid + 1, hi, aux);
|
|
if (compare(a[mid], a[mid + 1]) <= 0) {
|
|
// left and right are sorted and if the last-left element is less
|
|
// or equals than the first-right element there is nothing else
|
|
// to do
|
|
return;
|
|
}
|
|
_merge(a, compare, lo, mid, hi, aux);
|
|
}
|
|
export function groupBy(data, compare) {
|
|
var result = [];
|
|
var currentGroup = undefined;
|
|
for (var _i = 0, _a = mergeSort(data.slice(0), compare); _i < _a.length; _i++) {
|
|
var element = _a[_i];
|
|
if (!currentGroup || compare(currentGroup[0], element) !== 0) {
|
|
currentGroup = [element];
|
|
result.push(currentGroup);
|
|
}
|
|
else {
|
|
currentGroup.push(element);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
* @returns a new array with all falsy values removed. The original array IS NOT modified.
|
|
*/
|
|
export function coalesce(array) {
|
|
if (!array) {
|
|
return array;
|
|
}
|
|
return array.filter(function (e) { return !!e; });
|
|
}
|
|
/**
|
|
* @returns false if the provided object is an array and not empty.
|
|
*/
|
|
export function isFalsyOrEmpty(obj) {
|
|
return !Array.isArray(obj) || obj.length === 0;
|
|
}
|
|
/**
|
|
* @returns True if the provided object is an array and has at least one element.
|
|
*/
|
|
export function isNonEmptyArray(obj) {
|
|
return Array.isArray(obj) && obj.length > 0;
|
|
}
|
|
/**
|
|
* Removes duplicates from the given array. The optional keyFn allows to specify
|
|
* how elements are checked for equalness by returning a unique string for each.
|
|
*/
|
|
export function distinct(array, keyFn) {
|
|
if (!keyFn) {
|
|
return array.filter(function (element, position) {
|
|
return array.indexOf(element) === position;
|
|
});
|
|
}
|
|
var seen = Object.create(null);
|
|
return array.filter(function (elem) {
|
|
var key = keyFn(elem);
|
|
if (seen[key]) {
|
|
return false;
|
|
}
|
|
seen[key] = true;
|
|
return true;
|
|
});
|
|
}
|
|
export function distinctES6(array) {
|
|
var seen = new Set();
|
|
return array.filter(function (element) {
|
|
if (seen.has(element)) {
|
|
return false;
|
|
}
|
|
seen.add(element);
|
|
return true;
|
|
});
|
|
}
|
|
export function firstIndex(array, fn) {
|
|
for (var i = 0; i < array.length; i++) {
|
|
var element = array[i];
|
|
if (fn(element)) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
export function first(array, fn, notFoundValue) {
|
|
if (notFoundValue === void 0) { notFoundValue = undefined; }
|
|
var index = firstIndex(array, fn);
|
|
return index < 0 ? notFoundValue : array[index];
|
|
}
|
|
export function flatten(arr) {
|
|
var _a;
|
|
return (_a = []).concat.apply(_a, arr);
|
|
}
|
|
export function range(arg, to) {
|
|
var from = typeof to === 'number' ? arg : 0;
|
|
if (typeof to === 'number') {
|
|
from = arg;
|
|
}
|
|
else {
|
|
from = 0;
|
|
to = arg;
|
|
}
|
|
var result = [];
|
|
if (from <= to) {
|
|
for (var i = from; i < to; i++) {
|
|
result.push(i);
|
|
}
|
|
}
|
|
else {
|
|
for (var i = from; i > to; i--) {
|
|
result.push(i);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
* Insert `insertArr` inside `target` at `insertIndex`.
|
|
* Please don't touch unless you understand https://jsperf.com/inserting-an-array-within-an-array
|
|
*/
|
|
export function arrayInsert(target, insertIndex, insertArr) {
|
|
var before = target.slice(0, insertIndex);
|
|
var after = target.slice(insertIndex);
|
|
return before.concat(insertArr, after);
|
|
}
|
|
/**
|
|
* Pushes an element to the start of the array, if found.
|
|
*/
|
|
export function pushToStart(arr, value) {
|
|
var index = arr.indexOf(value);
|
|
if (index > -1) {
|
|
arr.splice(index, 1);
|
|
arr.unshift(value);
|
|
}
|
|
}
|
|
/**
|
|
* Pushes an element to the end of the array, if found.
|
|
*/
|
|
export function pushToEnd(arr, value) {
|
|
var index = arr.indexOf(value);
|
|
if (index > -1) {
|
|
arr.splice(index, 1);
|
|
arr.push(value);
|
|
}
|
|
}
|
|
export function asArray(x) {
|
|
return Array.isArray(x) ? x : [x];
|
|
}
|