UNPKG

@devexperts/dxcharts-lite

Version:
275 lines (274 loc) 8.68 kB
/* * 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); }