UNPKG

@rimbu/base

Version:

Utilities to implement Rimbu collections

313 lines (256 loc) 7.9 kB
import { TraverseState, Update, type ArrayNonEmpty } from '@rimbu/common'; export function _appendNew<T>(array: readonly T[], value: T): ArrayNonEmpty<T> { return array.toSpliced(array.length, 0, value) as ArrayNonEmpty<T>; } export function _appendOld<T>(array: readonly T[], value: T): ArrayNonEmpty<T> { const clone = array.slice(); clone.push(value); return clone as ArrayNonEmpty<T>; } // Returns a copy of the array with the given value appended export const append = `toSpliced` in Array.prototype ? _appendNew : _appendOld; // Returns the concatenation of the two arrays, potentially reusing the input array if one of the arrays is empty export function concat<T>( first: readonly T[], second: readonly T[] ): readonly T[] { if (first.length === 0) return second; if (second.length === 0) return first; return first.concat(second); } export function _reverseNew<T>( array: readonly T[], start?: number, end?: number ): T[] { const source = undefined !== start || undefined !== end ? array.slice(start ?? 0, (end ?? array.length - 1) + 1) : array; return source.toReversed(); } export function _reverseOld<T>( array: readonly T[], start?: number, end?: number ): T[] { const _start = start ?? 0; const _end = end ?? array.length - 1; const length = _end - _start + 1; const res = [] as T[]; let arrayIndex = _start - 1; let resIndex = length - 1; while (++arrayIndex <= _end) res[resIndex--] = array[arrayIndex]; return res; } // Returns an copy of the array between the start and end indices, with the elements in reversed order. export const reverse = 'toReversed' in Array.prototype ? _reverseNew : _reverseOld; // Performs given function on each element of the array, in reverse order if 'reversed' is true. export function forEach<T>( array: readonly T[], f: (value: T, index: number, halt: () => void) => void, state: TraverseState = TraverseState(), reversed = false ): void { if (state.halted) return; const { halt } = state; if (reversed) { let i = array.length; while (!state.halted && --i >= 0) { f(array[i], state.nextIndex(), halt); } } else { const length = array.length; let i = -1; while (!state.halted && ++i < length) { f(array[i], state.nextIndex(), halt); } } } // Returns a copy of the array where given function is applied to each element export function map<T, R>( array: readonly T[], f: (value: T, index: number) => R, indexOffset = 0 ): R[] { if (indexOffset === 0) { // without offset, can use standard array map return array.map(f); } const result: R[] = []; let index = indexOffset; let i = -1; const length = array.length; while (++i < length) { result[i] = f(array[i], index++); } return result; } // Returns a copy of the array where given functio is applied to each element in reverse order export function reverseMap<T, R>( array: readonly T[], f: (value: T, index: number) => R, indexOffset = 0 ): R[] { const result: R[] = []; let index = indexOffset; let arrayIndex = array.length; let resultIndex = 0; while (--arrayIndex >= 0) result[resultIndex++] = f(array[arrayIndex], index++); return result; } export function _prependNew<T>( array: readonly T[], value: T ): ArrayNonEmpty<T> { return array.toSpliced(0, 0, value) as ArrayNonEmpty<T>; } export function _prependOld<T>( array: readonly T[], value: T ): ArrayNonEmpty<T> { const clone = array.slice(); clone.unshift(value); return clone as ArrayNonEmpty<T>; } // Returns a copy of the given array with the given value added at the start export const prepend = `toSpliced` in Array.prototype ? _prependNew : _prependOld; export function _lastNew<T>(arr: readonly T[]): T { return arr.at(-1)!; } export function _lastOld<T>(arr: readonly T[]): T { return arr[arr.length - 1]; } // Returns the last element of the array export const last = `at` in Array.prototype ? _lastNew : _lastOld; export function _updateNew<T>( arr: readonly T[], index: number, updater: Update<T> ): readonly T[] { if (index < 0 || index >= arr.length) { return arr; } const curValue = arr[index]; const newValue = Update(curValue, updater); if (Object.is(newValue, curValue)) { return arr; } return arr.with(index, newValue); } export function _updateOld<T>( arr: readonly T[], index: number, updater: Update<T> ): readonly T[] { if (index < 0 || index >= arr.length) { return arr; } const curValue = arr[index]; const newValue = Update(curValue, updater); if (Object.is(newValue, curValue)) { return arr; } const newArr = arr.slice(); newArr[index] = newValue; return newArr; } // Returns a copy of the array where the element at given index is replaced by the given updater. // If the new element is the same as the old element, the original array is returned export const update = `with` in Array.prototype ? _updateNew : _updateOld; export function _modNew<T>( arr: readonly T[], index: number, f: (value: T) => T ): readonly T[] { if (index < 0 || index >= arr.length) { return arr; } const curValue = arr[index]; const newValue = f(curValue); if (Object.is(newValue, curValue)) { return arr; } return arr.with(index, newValue); } export function _modOld<T>( arr: readonly T[], index: number, f: (value: T) => T ): readonly T[] { if (index < 0 || index >= arr.length) { return arr; } const curValue = arr[index]; const newValue = f(curValue); if (Object.is(newValue, curValue)) { return arr; } const newArr = arr.slice(); newArr[index] = newValue; return newArr; } // Returns a copy of the array where the element at given index is replaced by applying given function. // If the new element is the same as the old element, the original array is returned export const mod = `with` in Array.prototype ? _modNew : _modOld; export function _insertNew<T>(arr: readonly T[], index: number, value: T): T[] { return arr.toSpliced(index, 0, value); } export function _insertOld<T>(arr: readonly T[], index: number, value: T): T[] { const clone = arr.slice(); clone.splice(index, 0, value); return clone; } // Returns a copy of the array where at given index the given value is inserted export const insert = `toSpliced` in Array.prototype ? _insertNew : _insertOld; // Returns a copy of the array, without its first element export function tail<T>(arr: readonly T[]): T[] { return arr.slice(1); } // Returns a copy of the array, without its last element export function init<T>(arr: readonly T[]): T[] { return arr.slice(0, arr.length - 1); } export function _spliceNew<T>( arr: readonly T[], start: number, deleteCount: number, ...items: T[] ): T[] { return arr.toSpliced(start, deleteCount, ...items); } export function _spliceOld<T>( arr: readonly T[], start: number, deleteCount: number, ...items: T[] ): T[] { const clone = arr.slice(); clone.splice(start, deleteCount, ...items); return clone; } // Immutable version of the array .splice command, always returns a new array export const splice = `toSpliced` in Array.prototype ? _spliceNew : _spliceOld; // Returns a copy of the array, where its 'sparse' property is kept (sparse = not all indices have a value) export function copySparse<T>(arr: readonly T[]): T[] { const clone: T[] = []; for (const key in arr) { clone[key] = arr[key]; } return clone; } // Returns a copy of the array with given function applied to each element, where its 'sparse' property is kept // (sparse = not all indices have a value) export function mapSparse<T, T2>( arr: readonly T[], f: (value: T, index: number) => T2 ): T2[] { const result: T2[] = Array(arr.length); for (const key in arr) { result[key] = f(arr[key], key as any); } return result; }