@devexperts/dxcharts-lite
Version:
275 lines (274 loc) • 8.68 kB
JavaScript
/*
* Copyright (C) 2019 - 2025 Devexperts Solutions IE Limited
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { identity } from './function.utils';
import { clamp, shiftRight } from './math.utils';
/**
* Intersects 2 arrays.
* Example:
* source: [1, 2, 3, 1]
* target: [1, 2, 4, 5, 6]
* result: [1, 2]
* result (with duplicates): [1, 1, 2]
* @param source
* @param target
* @param compareFn
* @param removeDuplicates
* @doc-tags fp,utility
*/
export function arrayIntersect(source, target, compareFn = eqeqeq, removeDuplicates = true) {
const intersection = source.filter(a => target.some(b => compareFn(a, b)));
if (removeDuplicates) {
return intersection.filter((value, index, self) => self.indexOf(value) === index);
}
return intersection;
}
/**
* Reorders source array according to newOrder.
* Returns a reorder copy of the source array.
* Example:
* source: ['1', '2', '3']
* newOrder: ['3', '1']
* result: ['3', '2', '1']
*/
export const reorderArray = (source, newOrder) => {
// filter out elements which are not presented in the source array
const order = newOrder.slice().filter(i => source.find(s => s === i));
return source.map(el => {
var _a;
if (newOrder.indexOf(el) >= 0) {
return (_a = order.shift()) !== null && _a !== void 0 ? _a : el;
}
return el;
});
};
export const eqeqeq = (a, b) => a === b;
/**
* Subtracts target from source array.
* Example:
* source: [1, 2, 3, 4]
* target: [2, 4, 6]
* result: [1, 3]
* @param source
* @param target
* @param compareFn
*/
export const arraySubtract = (source, target, compareFn) => {
return source.filter(a => target.filter(b => compareFn(a, b)).length === 0);
};
const eqStrict = (a, b) => a === b;
export const arrayCompare = (source, target, compareFn = eqStrict) => {
return source.length === target.length && source.every((w, i) => compareFn(w, target[i]));
};
export function moveInArray(arr, from, to) {
const result = arr.slice();
const item = result[from];
result.splice(from, 1);
result.splice(to, 0, item);
return result;
}
export function moveInArrayMutable(arr, _from, _to) {
const n = arr.length;
const from = clamp(_from, 0, n - 1);
const to = clamp(_to, 0, n - 1);
const item = arr[from];
arr.splice(from, 1);
arr.splice(to, 0, item);
return arr;
}
/**
* Replaces item in array with another one.
* @param arr
* @param itemFinder - predicate to find item
* @param replace - item to replace
*/
export function replaceInArray(arr, itemFinder, replace) {
const result = arr.slice();
const idx = result.findIndex(itemFinder);
if (idx !== -1) {
result.splice(idx, 1);
result.splice(idx, 0, replace);
}
return result;
}
export const uniqueArray = (arr) => {
const hashTable = {};
return arr.filter(item => (hashTable[item] ? false : (hashTable[item] = true)));
};
export const groupBy = (array, key) => {
return array.reduce((result, currentValue) => {
// If an array already present for key, push it to the array. Else create an array and push the object
(result[currentValue[key]] = result[currentValue[key]] || []).push(currentValue);
// Return the current iteration `result` value, this will be taken as next iteration `result` value and accumulate
return result;
// eslint-disable-next-line no-restricted-syntax
}, {}); // empty object is the initial value for result object
};
/**
* Inserts *something* between every two elements in the source array
* Example: interleave(['first', 'second', 'third'], 'bar') => ['first', 'bar', 'second', 'bar', 'third']
* @param arr
* @param something
*/
export const interleave = (arr, something) =>
// eslint-disable-next-line no-restricted-syntax
[].concat(...arr.map(n => [n, something])).slice(0, -1);
export const isTwoDimArray = (arr) => Array.isArray(arr[0]);
// @ts-ignore
export const create2DArray = (arr) => (Array.isArray(arr[0]) ? arr : [arr]);
export const slice2DArray = (arr, startIdx, endIdx) => {
const result = [];
let pointsCurrentIdx = 0;
for (const arr1D of arr) {
const localIdxStart = Math.max(startIdx - pointsCurrentIdx, 0);
const localEndIdx = Math.max(endIdx - pointsCurrentIdx, -1);
const newPoints = arr1D.slice(localIdxStart, localEndIdx + 1);
newPoints.length > 0 && result.push(newPoints);
pointsCurrentIdx += arr1D.length;
}
return result;
};
// Array.at() polyfill (as of December 2022 method is not supported on iOS <= v15)
export const at = (idx, arr) => (idx >= 0 ? arr[idx] : arr[arr.length + idx]);
// Array.flat() polyfill (support for chrome 66)
export const flat = (arr) => {
// @ts-ignore
if (Array.prototype.flat) {
return arr.flat();
}
return flatMap(arr, identity);
};
// Array.flatMap() polyfill (support for chrome 66)
export const flatMap = (arr, callback) => {
// @ts-ignore
if (Array.prototype.flatMap) {
return arr.flatMap(callback);
}
const result = [];
for (let i = 0; i < arr.length; i++) {
const item = arr[i];
result.push(...callback(item, i, arr));
}
return result;
};
// eslint-disable-next-line no-redeclare
export function binarySearch(array, what, transform) {
// @ts-ignore
const applyTransform = transform !== null && transform !== void 0 ? transform : identity;
if (!array.length || what !== what) {
return {
index: -1,
exact: true,
};
}
return binarySearchFn(array, what, 0, array.length, applyTransform);
}
function binarySearchFn(array, what, from, to, transform) {
const center = shiftRight(from + to, 1);
const centerValue = transform(array[center]);
if (what === centerValue) {
return {
index: center,
exact: true,
};
}
else {
if (from === center) {
return {
index: from,
exact: false,
};
}
else {
if (what < centerValue) {
return binarySearchFn(array, what, from, center, transform);
}
else {
return binarySearchFn(array, what, center, to, transform);
}
}
}
}
export function lastOf(arr) {
if (arr && arr.length) {
return arr[arr.length - 1];
}
}
export function arrayRemove(element) {
const idx = this.indexOf(element);
if (idx !== -1) {
this.splice(idx, 1);
}
return this;
}
// ... and this is how a normal person would write "arrayRemove" function
export function arrayRemove2(array, element) {
if (!array) {
return;
}
const idx = array.indexOf(element);
if (idx !== -1) {
array.splice(idx, 1);
}
}
export function firstOf(arr) {
if (arr) {
return arr[0];
}
}
/**
@param {Function} gather - get the comparing value from the array
*/
export function maxMin(gather) {
function reducer(a, b) {
const v = gather(b);
if (v === null || v === undefined || !isFinite(v)) {
return a;
}
else {
return [Math.max(a[0], v), Math.min(a[1], v)];
}
}
return function (arr) {
const defaultResult = [Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY];
if (arr) {
return arr.reduce(reducer, defaultResult);
}
return defaultResult;
};
}
export function getElementsInRange(array, leftBound, rightBound, transform = () => 0) {
const upperIndex = (arr, bound) => {
let l = 0;
let h = arr.length - 1;
while (h >= l) {
const midIndex = Math.ceil((l + h) / 2);
const mid = transform(arr[midIndex]);
if (mid <= bound) {
l = midIndex + 1;
}
else {
h = midIndex - 1;
}
}
return h;
};
const lowerIndex = (arr, bound) => {
let l = 0;
let h = arr.length - 1;
while (h >= l) {
const midIndex = Math.ceil((l + h) / 2);
const mid = transform(arr[midIndex]);
if (mid >= bound) {
h = midIndex - 1;
}
else {
l = midIndex + 1;
}
}
return l;
};
return array.slice(lowerIndex(array, leftBound), upperIndex(array, rightBound) + 1);
}