Assist_Design/packages/shared/src/array-utils.ts

118 lines
2.8 KiB
TypeScript
Raw Normal View History

/**
* Modern array utilities leveraging ES2024 features
*/
/**
* Group array items by a key using ES2024 Object.groupBy
* This is more efficient than manual grouping
*/
export function groupBy<T, K extends PropertyKey>(
array: T[],
keyFn: (item: T) => K
): Record<K, T[]> {
// Use native Object.groupBy if available (ES2024)
if ('groupBy' in Object) {
return (Object as any).groupBy(array, keyFn);
}
// Fallback polyfill
return array.reduce((groups, item) => {
const key = keyFn(item);
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(item);
return groups;
}, {} as Record<K, T[]>);
}
/**
* Chunk array into smaller arrays of specified size
* Uses modern array methods for efficiency
*/
export function chunk<T>(array: T[], size: number): T[][] {
if (size <= 0) return [];
const chunks: T[][] = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
/**
* Get unique items from array with optional key function
* Leverages Set for better performance
*/
export function unique<T>(array: T[]): T[];
export function unique<T, K>(array: T[], keyFn: (item: T) => K): T[];
export function unique<T, K>(array: T[], keyFn?: (item: T) => K): T[] {
if (!keyFn) {
return [...new Set(array)];
}
const seen = new Set<K>();
return array.filter(item => {
const key = keyFn(item);
if (seen.has(key)) {
return false;
}
seen.add(key);
return true;
});
}
/**
* Safe array access using ES2024 .at() method
* Returns undefined for out-of-bounds access
*/
export function safeAt<T>(array: T[], index: number): T | undefined {
return array.at?.(index) ?? array[index];
}
/**
* Partition array into two arrays based on predicate
*/
export function partition<T>(
array: T[],
predicate: (item: T) => boolean
): [T[], T[]] {
const truthy: T[] = [];
const falsy: T[] = [];
for (const item of array) {
if (predicate(item)) {
truthy.push(item);
} else {
falsy.push(item);
}
}
return [truthy, falsy];
}
/**
* Modern intersection of arrays
*/
export function intersection<T>(...arrays: T[][]): T[] {
if (arrays.length === 0) return [];
if (arrays.length === 1) return [...arrays[0]];
const [first, ...rest] = arrays;
const sets = rest.map(arr => new Set(arr));
return first.filter(item => sets.every(set => set.has(item)));
}
/**
* Safe array operations that return null on empty arrays
*/
export const SafeArray = {
first: <T>(array: T[]): T | null => array[0] ?? null,
last: <T>(array: T[]): T | null => array.at?.(-1) ?? array[array.length - 1] ?? null,
random: <T>(array: T[]): T | null => {
if (array.length === 0) return null;
return array[Math.floor(Math.random() * array.length)];
}
} as const;