All files / packages/utils/src array.ts

70.27% Statements 26/37
52.17% Branches 12/23
54.54% Functions 6/11
70.27% Lines 26/37

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104    242x 1397x 1397x         242x           56x     56x 56x   56x     242x           44x 44x       44x     242x             12x 12x               242x         31x 31x 1x   30x 30x           31x     242x       21x     6x         6x                                    
import type { TupleOf } from "./types";
 
export const rotate = <T>(array: T[], by: number): T[] => {
	const byConstrained = by % array.length;
	return array.slice(byConstrained).concat(array.slice(0, byConstrained));
};
 
export type ItemWithIndex<T> = { index: number; item: T };
 
const replace = <T>(
	array: T[],
	item: T,
	index: number,
	ref?: React.RefObject<T | undefined>,
) => {
	Iif (array[index] === item) {
		return array;
	}
	Eif (ref) {
		ref.current = array[index];
	}
	return [...array.slice(0, index), item, ...array.slice(index + 1)];
};
 
export const replaceInArray = <T>(
	array: T[],
	predicate: (item: T, index: number, items: T[]) => boolean,
	updater: (prevItem: T) => T,
	ref?: React.RefObject<T | undefined>,
) => {
	const matchedIndex = array.findIndex(predicate);
	Iif (matchedIndex === -1) {
		return array;
	}
	// oxlint-disable-next-line typescript/no-non-null-assertion
	return replace(array, updater(array[matchedIndex]!), matchedIndex, ref);
};
 
export const upsertInArray = <T>(
	array: T[],
	predicate: (item: T, index: number, items: T[]) => boolean,
	updater: (prevItem: T) => T,
	defaultValue: T,
	ref?: React.RefObject<T | undefined>,
) => {
	const matchedIndex = array.findIndex(predicate);
	return replace(
		array,
		updater((array[matchedIndex] ?? undefined) || defaultValue),
		matchedIndex === -1 ? Infinity : matchedIndex,
		ref,
	);
};
 
export const removeFromArray = <T>(
	array: T[],
	predicate: (item: T, index: number, items: T[]) => boolean,
	ref?: React.RefObject<ItemWithIndex<T> | undefined>,
) => {
	const matchedIndex = array.findIndex(predicate);
	if (matchedIndex === -1) {
		return array;
	}
	Eif (ref) {
		ref.current = {
			index: matchedIndex,
			// oxlint-disable-next-line typescript/no-non-null-assertion
			item: array[matchedIndex]!,
		};
	}
	return [...array.slice(0, matchedIndex), ...array.slice(matchedIndex + 1)];
};
 
export const addToArray = <T>(
	array: T[],
	item: T,
	index: number = array.length - 1,
) => [...array.slice(0, index), item, ...array.slice(index)];
 
export const asFixedSizeArray =
	<N extends number>() =>
	<T>(array: T[]) =>
		array as TupleOf<T, N>;
 
export type Interval = [number, number];
export const mergeIntervals = (
	intervals: Interval[],
	connectAdjacentInts = false,
) =>
	intervals
		.toSorted((a, b) => a[0] - b[0])
		.reduce<Interval[]>((acc, [from, to]) => {
			const lastInterval = acc.at(-1);
			if (
				!lastInterval ||
				lastInterval[1] + (connectAdjacentInts ? 1 : 0) < from
			) {
				acc.push([from, to]);
			} else {
				lastInterval[1] = Math.max(lastInterval[1], to);
			}
			return acc;
		}, []);