import type { MaybePromise, MaybeReadonly } from 'nxdb-old/src/types'; export function lastOfArray(ar: T[]): T | undefined { return ar[ar.length - 1]; } /** * shuffle the given array */ export function shuffleArray(arr: T[]): T[] { return arr.slice(0).sort(() => (Math.random() - 0.5)); } export function toArray(input: T | T[] | Readonly | Readonly): T[] { return Array.isArray(input) ? (input as any[]).slice(0) : [input]; } /** * Split array with items into smaller arrays with items * @link https://stackoverflow.com/a/7273794/3443137 */ export function batchArray(array: T[], batchSize: number): T[][] { array = array.slice(0); const ret: T[][] = []; while (array.length) { const batch = array.splice(0, batchSize); ret.push(batch); } return ret; } /** * @link https://stackoverflow.com/a/15996017 */ export function removeOneFromArrayIfMatches(ar: T[], condition: (x: T) => boolean): T[] { ar = ar.slice(); let i = ar.length; let done = false; while (i-- && !done) { if (condition(ar[i])) { done = true; ar.splice(i, 1); } } return ar; } /** * returns true if the supplied argument is either an Array or a Readonly> */ export function isMaybeReadonlyArray(x: any): x is MaybeReadonly { // While this looks strange, it's a workaround for an issue in TypeScript: // https://github.com/microsoft/TypeScript/issues/17002 // // The problem is that `Array.isArray` as a type guard returns `false` for a readonly array, // but at runtime the object is an array and the runtime call to `Array.isArray` would return `true`. // The type predicate here allows for both `Array` and `Readonly>` to pass a type check while // still performing runtime type inspection. return Array.isArray(x); } /** * Use this in array.filter() to remove all empty slots * and have the correct typings afterwards. * @link https://stackoverflow.com/a/46700791/3443137 */ export function arrayFilterNotEmpty(value: TValue | null | undefined): value is TValue { if (value === null || value === undefined) { return false; } return true; } export function countUntilNotMatching( ar: T[], matchingFn: (v: T, idx: number) => boolean ): number { let count = 0; let idx = -1; for (const item of ar) { idx = idx + 1; const matching = matchingFn(item, idx); if (matching) { count = count + 1; } else { break; } } return count; } export async function asyncFilter(array: T[], predicate: (item: T, index: number, a: T[]) => MaybePromise): Promise { const filters = await Promise.all( array.map(predicate) ); return array.filter((...[, index]) => filters[index]); } /** * @link https://stackoverflow.com/a/3762735 */ export function sumNumberArray(array: number[]): number { let count = 0; for (let i = array.length; i--;) { count += array[i]; } return count; } export function maxOfNumbers(arr: number[]): number { return Math.max(...arr); } /** * Appends the given documents to the given array. * This will mutate the first given array. * Mostly used as faster alternative to Array.concat() * because .concat() is so slow. * @link https://www.measurethat.net/Benchmarks/Show/4223/0/array-concat-vs-spread-operator-vs-push#latest_results_block */ export function appendToArray(ar: T[], add: T[] | readonly T[]): void { const amount = add.length; for (let i = 0; i < amount; ++i) { const element = add[i]; ar.push(element); } }