src/helpers/helpers.collection.ts
import {_capitalize} from './helpers.core.js';
/**
* Binary search
* @param table - the table search. must be sorted!
* @param value - value to find
* @param cmp
* @private
*/
export function _lookup(
table: number[],
value: number,
cmp?: (value: number) => boolean
): {lo: number, hi: number};
export function _lookup<T>(
table: T[],
value: number,
cmp: (value: number) => boolean
): {lo: number, hi: number};
export function _lookup(
table: unknown[],
value: number,
cmp?: (value: number) => boolean
) {
cmp = cmp || ((index) => table[index] < value);
let hi = table.length - 1;
let lo = 0;
let mid: number;
while (hi - lo > 1) {
mid = (lo + hi) >> 1;
if (cmp(mid)) {
lo = mid;
} else {
hi = mid;
}
}
return {lo, hi};
}
/**
* Binary search
* @param table - the table search. must be sorted!
* @param key - property name for the value in each entry
* @param value - value to find
* @param last - lookup last index
* @private
*/
export const _lookupByKey = (
table: Record<string, number>[],
key: string,
value: number,
last?: boolean
) =>
_lookup(table, value, last
? index => {
const ti = table[index][key];
return ti < value || ti === value && table[index + 1][key] === value;
}
: index => table[index][key] < value);
/**
* Reverse binary search
* @param table - the table search. must be sorted!
* @param key - property name for the value in each entry
* @param value - value to find
* @private
*/
export const _rlookupByKey = (
table: Record<string, number>[],
key: string,
value: number
) =>
_lookup(table, value, index => table[index][key] >= value);
/**
* Return subset of `values` between `min` and `max` inclusive.
* Values are assumed to be in sorted order.
* @param values - sorted array of values
* @param min - min value
* @param max - max value
*/
export function _filterBetween(values: number[], min: number, max: number) {
let start = 0;
let end = values.length;
while (start < end && values[start] < min) {
start++;
}
while (end > start && values[end - 1] > max) {
end--;
}
return start > 0 || end < values.length
? values.slice(start, end)
: values;
}
const arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'] as const;
export interface ArrayListener<T> {
_onDataPush?(...item: T[]): void;
_onDataPop?(): void;
_onDataShift?(): void;
_onDataSplice?(index: number, deleteCount: number, ...items: T[]): void;
_onDataUnshift?(...item: T[]): void;
}
/**
* Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
* 'unshift') and notify the listener AFTER the array has been altered. Listeners are
* called on the '_onData*' callbacks (e.g. _onDataPush, etc.) with same arguments.
*/
export function listenArrayEvents<T>(array: T[], listener: ArrayListener<T>): void;
export function listenArrayEvents(array, listener) {
if (array._chartjs) {
array._chartjs.listeners.push(listener);
return;
}
Object.defineProperty(array, '_chartjs', {
configurable: true,
enumerable: false,
value: {
listeners: [listener]
}
});
arrayEvents.forEach((key) => {
const method = '_onData' + _capitalize(key);
const base = array[key];
Object.defineProperty(array, key, {
configurable: true,
enumerable: false,
value(...args) {
const res = base.apply(this, args);
array._chartjs.listeners.forEach((object) => {
if (typeof object[method] === 'function') {
object[method](...args);
}
});
return res;
}
});
});
}
/**
* Removes the given array event listener and cleanup extra attached properties (such as
* the _chartjs stub and overridden methods) if array doesn't have any more listeners.
*/
export function unlistenArrayEvents<T>(array: T[], listener: ArrayListener<T>): void;
export function unlistenArrayEvents(array, listener) {
const stub = array._chartjs;
if (!stub) {
return;
}
const listeners = stub.listeners;
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
if (listeners.length > 0) {
return;
}
arrayEvents.forEach((key) => {
delete array[key];
});
delete array._chartjs;
}
/**
* @param items
*/
export function _arrayUnique<T>(items: T[]) {
const set = new Set<T>(items);
if (set.size === items.length) {
return items;
}
return Array.from(set);
}