/** * @fileoverview Data manipulation and array utilities * @module data */ /** * Swaps two elements in an array. * * @example * ```ts * const arr = [1, 2, 3, 4]; * swap(arr, 0, 3); * console.log(arr); // [4, 2, 3, 1] * ``` */ export function swap(array: T[], a: number, b: number): void { const temp = array[a]; array[a] = array[b]!; array[b] = temp!; } /** * Splits an array into chunks of specified size. * * @example * ```ts * chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]] * ``` */ export function chunk(array: T[], size: number): T[][] { if (size <= 0) throw new Error("Chunk size must be positive"); const result: T[][] = []; for (let i = 0; i < array.length; i += size) { result.push(array.slice(i, i + size)); } return result; } /** * Flattens a nested array to a specified depth. * * @example * ```ts * flatten([[1, 2], [3, [4, 5]]], 1); // [1, 2, 3, [4, 5]] * flatten([[1, 2], [3, [4, 5]]], 2); // [1, 2, 3, 4, 5] * ``` */ export function flatten(array: unknown[], depth = 1): T[] { const result: T[] = []; function flattenHelper(arr: unknown[], currentDepth: number): void { for (const item of arr) { if (Array.isArray(item) && currentDepth > 0) { flattenHelper(item, currentDepth - 1); } else { result.push(item as T); } } } flattenHelper(array, depth); return result; } /** * Returns unique elements from an array. * * @example * ```ts * unique([1, 2, 2, 3, 3, 3]); // [1, 2, 3] * unique(['a', 'b', 'a', 'c']); // ['a', 'b', 'c'] * ``` */ export function unique(array: T[]): T[] { return [...new Set(array)]; } /** * Groups array elements by a key function. * * @example * ```ts * const people = [ * { name: 'Alice', age: 25 }, * { name: 'Bob', age: 30 }, * { name: 'Carol', age: 25 } * ]; * groupBy(people, p => p.age); * // Map(2) { 25 => [Alice, Carol], 30 => [Bob] } * ``` */ export function groupBy(array: T[], keyFn: (item: T) => K): Map { const groups = new Map(); for (const item of array) { const key = keyFn(item); const group = groups.get(key) || []; group.push(item); groups.set(key, group); } return groups; } /** * Sorts array by a key function. * * @example * ```ts * const people = [ * { name: 'Carol', age: 25 }, * { name: 'Alice', age: 30 }, * { name: 'Bob', age: 20 } * ]; * sortBy(people, p => p.age); // [Bob(20), Carol(25), Alice(30)] * sortBy(people, p => p.name); // [Alice, Bob, Carol] * ``` */ export function sortBy(array: T[], keyFn: (item: T) => K): T[] { return [...array].sort((a, b) => { const keyA = keyFn(a); const keyB = keyFn(b); if (keyA < keyB) return -1; if (keyA > keyB) return 1; return 0; }); } /** * Removes element at specified index and returns it. * * @example * ```ts * const arr = [1, 2, 3, 4]; * const removed = removeAt(arr, 1); // removed = 2, arr = [1, 3, 4] * ``` */ export function removeAt(array: T[], index: number): T | undefined { if (index < 0 || index >= array.length) return undefined; return array.splice(index, 1)[0]; } /** * Inserts element at specified index. * * @example * ```ts * const arr = [1, 3, 4]; * insertAt(arr, 1, 2); // arr = [1, 2, 3, 4] * ``` */ export function insertAt(array: T[], index: number, item: T): void { array.splice(index, 0, item); } /** * Rotates array elements to the left by n positions. * * @example * ```ts * rotateLeft([1, 2, 3, 4, 5], 2); // [3, 4, 5, 1, 2] * ``` */ export function rotateLeft(array: T[], n: number): T[] { const len = array.length; if (len === 0) return array; n = n % len; if (n === 0) return [...array]; return [...array.slice(n), ...array.slice(0, n)]; } /** * Rotates array elements to the right by n positions. * * @example * ```ts * rotateRight([1, 2, 3, 4, 5], 2); // [4, 5, 1, 2, 3] * ``` */ export function rotateRight(array: T[], n: number): T[] { const len = array.length; if (len === 0) return array; n = n % len; if (n === 0) return [...array]; return [...array.slice(-n), ...array.slice(0, -n)]; } /** * Performs binary search on a sorted array. * * @example * ```ts * binarySearch([1, 3, 5, 7, 9], 5); // 2 * binarySearch([1, 3, 5, 7, 9], 6); // -1 * ``` */ export function binarySearch( array: T[], target: T, compareFn?: (a: T, b: T) => number, ): number { const compare = compareFn || ((a, b) => a < b ? -1 : a > b ? 1 : 0); let left = 0; let right = array.length - 1; while (left <= right) { const mid = Math.floor((left + right) / 2); const comparison = compare(array[mid]!, target); if (comparison === 0) return mid; if (comparison < 0) left = mid + 1; else right = mid - 1; } return -1; } /** * Partitions array into two arrays based on predicate. * * @example * ```ts * const [evens, odds] = partition([1, 2, 3, 4, 5], x => x % 2 === 0); * // evens = [2, 4], odds = [1, 3, 5] * ``` */ export function partition( 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]; } /** * Finds the intersection of multiple arrays. * * @example * ```ts * intersect([1, 2, 3], [2, 3, 4], [3, 4, 5]); // [3] * ``` */ export function intersect(...arrays: T[][]): T[] { if (arrays.length === 0) return []; if (arrays.length === 1) return unique(arrays[0]!); const sets = arrays.map((arr) => new Set(arr)); const [first, ...rest] = sets; return Array.from(first!).filter((item) => rest.every((set) => set.has(item)) ); } /** * Finds the difference between arrays (elements in first array but not in others). * * @example * ```ts * difference([1, 2, 3, 4], [2, 3], [3, 4]); // [1] * ``` */ export function difference(array: T[], ...others: T[][]): T[] { const othersSet = new Set(others.flat()); return array.filter((item) => !othersSet.has(item)); } /** * Performs a deep equality check between two values. * * @example * ```ts * deepEqual([1, [2, 3]], [1, [2, 3]]); // true * deepEqual({a: {b: 1}}, {a: {b: 1}}); // true * ``` */ export function deepEqual(a: unknown, b: unknown): boolean { if (a === b) return true; if (a === null || b === null) return false; if (typeof a !== typeof b) return false; if (typeof a !== "object") return false; if (Array.isArray(a) !== Array.isArray(b)) return false; if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (!deepEqual(a[i], b[i])) return false; } return true; } const keysA = Object.keys(a as object); const keysB = Object.keys(b as object); if (keysA.length !== keysB.length) return false; for (const key of keysA) { if (!keysB.includes(key)) return false; if ( !deepEqual( (a as Record)[key], (b as Record)[key], ) ) return false; } return true; }