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

109 lines
2.6 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[],
2025-08-22 17:02:49 +09:00
keyFn: (item: T) => K
): Record<K, T[]> {
2025-08-21 15:24:40 +09:00
return array.reduce(
(groups, item) => {
const key = keyFn(item);
2025-08-22 17:02:49 +09:00
(groups[key] ??= []).push(item);
2025-08-21 15:24:40 +09:00
return groups;
},
2025-08-22 17:02:49 +09:00
{} as Record<K, T[]>
2025-08-21 15:24:40 +09:00
);
}
/**
* 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 [];
2025-08-21 15:24:40 +09:00
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)];
}
2025-08-21 15:24:40 +09:00
const seen = new Set<K>();
2025-08-22 17:02:49 +09:00
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
*/
2025-08-22 17:02:49 +09:00
export function partition<T>(array: T[], predicate: (item: T) => boolean): [T[], T[]] {
const truthy: T[] = [];
const falsy: T[] = [];
2025-08-21 15:24:40 +09:00
for (const item of array) {
if (predicate(item)) {
truthy.push(item);
} else {
falsy.push(item);
}
}
2025-08-21 15:24:40 +09:00
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]];
2025-08-21 15:24:40 +09:00
const [first, ...rest] = arrays;
2025-08-22 17:02:49 +09:00
const sets = rest.map(arr => new Set(arr));
2025-08-21 15:24:40 +09:00
2025-08-22 17:02:49 +09:00
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,
2025-08-22 17:02:49 +09:00
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)];
2025-08-21 15:24:40 +09:00
},
} as const;