UNPKG

@catbee/utils

Version:

A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.

369 lines 12.5 kB
var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; import { getValueByPath } from "./obj.utils"; /** * Splits an array into chunks of the specified size. * * @template T The type of array elements. * @param {T[]} array - The array to split into chunks. * @param {number} size - The number of elements per chunk. * @returns {T[][]} A new array containing chunked arrays. * @throws {TypeError} If array is not an array. * @throws {Error} If chunk size is not a positive integer. */ export function chunk(array, size) { if (!Array.isArray(array)) throw new TypeError("Expected an array"); if (!array.length) return []; if (!Number.isInteger(size) || size <= 0) throw new Error("Chunk size must be a positive integer"); return Array.from({ length: Math.ceil(array.length / size) }, function (_, i) { return array.slice(i * size, i * size + size); }); } /** * Removes duplicate values from an array. * Optionally enforces uniqueness by a key function. * * @template T The type of array elements. * @param {T[]} array - The input array. * @param {(item: T) => unknown} [keyFn] - Optional function to determine uniqueness by key. * @returns {T[]} A new array with unique values. */ export function unique(array, keyFn) { if (!Array.isArray(array) || array.length === 0) return []; if (!keyFn) return Array.from(new Set(array)); var seen = new Set(); return array.filter(function (item) { var key = keyFn(item); if (seen.has(key)) return false; seen.add(key); return true; }); } /** * Deeply flattens a nested array to a single-level array. * * @template T The leaf type of array elements. * @param {any[]} array - The (possibly deeply nested) input array. * @returns {T[]} A deeply flattened array. */ export function flattenDeep(array) { if (!Array.isArray(array)) return []; return array.reduce(function (acc, val) { if (Array.isArray(val)) { acc.push.apply(acc, __spreadArray([], __read(flattenDeep(val)), false)); } else { acc.push(val); } return acc; }, []); } /** * Returns a random element from an array, or undefined if empty. * * @template T The type of array elements. * @param {T[]} array - The input array. * @returns {T | undefined} A randomly selected item, or undefined if array is empty or not an array. */ export function random(array) { if (!Array.isArray(array) || array.length === 0) return undefined; return array[Math.floor(Math.random() * array.length)]; } export function groupBy(array, keyOrFn) { if (!Array.isArray(array) || array.length === 0) return {}; var keyFn = typeof keyOrFn === "function" ? keyOrFn : function (item) { return item[keyOrFn]; }; return array.reduce(function (acc, item) { var key = keyFn(item); if (!acc[key]) acc[key] = []; acc[key].push(item); return acc; }, {}); } /* eslint-enable no-redeclare */ /** * Shuffles an array using the Fisher-Yates algorithm. * * @template T The type of array elements. * @param {T[]} array - The input array. * @returns {T[]} A new shuffled array. * @throws {TypeError} If array is not an array. */ export function shuffle(array) { var _a; if (!Array.isArray(array)) throw new TypeError("Expected an array"); var copy = array.slice(); for (var i = copy.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); _a = __read([copy[j], copy[i]], 2), copy[i] = _a[0], copy[j] = _a[1]; } return copy; } /** * Returns an array of property values from an array of objects. * * @template T The type of array elements. * @template K The object property to pluck. * @param {T[]} array - The input array. * @param {K} key - The property name to pluck. * @returns {T[K][]} Array of property values. */ export function pluck(array, key) { if (!Array.isArray(array)) return []; return array.map(function (item) { return item[key]; }); } /** * Returns values in array A that are not in array B. * * @template T The type of array elements. * @param {T[]} a - First array. * @param {T[]} b - Second array. * @returns {T[]} Elements in A that are not in B. */ export function difference(a, b) { if (!Array.isArray(a) || !Array.isArray(b)) return []; var setB = new Set(b); return a.filter(function (item) { return !setB.has(item); }); } /** * Returns common values between arrays A and B. * * @template T The type of array elements. * @param {T[]} a - First array. * @param {T[]} b - Second array. * @returns {T[]} Elements that exist in both arrays. */ export function intersect(a, b) { if (!Array.isArray(a) || !Array.isArray(b)) return []; var setB = new Set(b); return a.filter(function (item) { return setB.has(item); }); } /** * Sorts an array of objects by a nested key using Merge Sort (O(n log n)). * Missing/undefined keys are sorted to the "end" (asc) or "start" (desc). * * @template T The type of array elements (objects). * @param {T[]} array - Array of objects to sort. * @param {string | ((item: T) => any)} key - Dot-notated key (e.g., "profile.age") or function. * @param {"asc" | "desc"} [direction="asc"] - Sort direction: 'asc' or 'desc'. * @returns {T[]} A new sorted array. * @throws {TypeError} If array is not an array. */ export function mergeSort(array, key, direction) { if (direction === void 0) { direction = "asc"; } if (!Array.isArray(array)) throw new TypeError("Expected array"); if (array.length <= 1) return array.slice(); var keyFn = typeof key === "function" ? key : function (item) { return getValueByPath(item, key); }; var compare = function (a, b) { var aVal = keyFn(a); var bVal = keyFn(b); // Sorts undefined/null last for "asc", first for "desc" if (aVal === bVal) return 0; if (aVal == null) return direction === "asc" ? 1 : -1; if (bVal == null) return direction === "asc" ? -1 : 1; return direction === "asc" ? (aVal < bVal ? -1 : 1) : aVal > bVal ? -1 : 1; }; var merge = function (left, right) { var result = []; var i = 0, j = 0; while (i < left.length && j < right.length) { if (compare(left[i], right[j]) <= 0) result.push(left[i++]); else result.push(right[j++]); } return result.concat(left.slice(i)).concat(right.slice(j)); }; var sort = function (arr) { if (arr.length <= 1) return arr; var mid = Math.floor(arr.length / 2); var left = sort(arr.slice(0, mid)); var right = sort(arr.slice(mid)); return merge(left, right); }; return sort(array); } /** * Combines multiple arrays into a single array of grouped elements. * Output array length equals the length of the shortest input array. * * @example zip([1, 2], ['a', 'b']) => [[1, 'a'], [2, 'b']] * @param {...Array<T>[]} arrays - Two or more arrays to zip together. * @returns {Array<T[]>} Array of grouped elements. */ export function zip() { var arrays = []; for (var _i = 0; _i < arguments.length; _i++) { arrays[_i] = arguments[_i]; } if (arrays.length === 0) return []; if (arrays.some(function (arr) { return !Array.isArray(arr); })) { throw new TypeError("All arguments must be arrays"); } var minLength = Math.min.apply(Math, __spreadArray([], __read(arrays.map(function (arr) { return arr.length; })), false)); var result = []; var _loop_1 = function (i) { result.push(arrays.map(function (arr) { return arr[i]; })); }; for (var i = 0; i < minLength; i++) { _loop_1(i); } return result; } /** * Splits an array into two arrays based on a predicate function. * * @template T The type of array elements. * @param {T[]} array - The input array. * @param {(item: T, index: number, array: T[]) => boolean} predicate - Function to test each element. * @returns {[T[], T[]]} A tuple of two arrays: [matched, unmatched]. */ export function partition(array, predicate) { if (!Array.isArray(array)) return [[], []]; return array.reduce(function (_a, item, index) { var _b = __read(_a, 2), pass = _b[0], fail = _b[1]; return predicate(item, index, array) ? [__spreadArray(__spreadArray([], __read(pass), false), [item], false), fail] : [pass, __spreadArray(__spreadArray([], __read(fail), false), [item], false)]; }, [[], []]); } /** * Generates an array of numbers within a specified range. * * @param {number} start - Start of range (inclusive). * @param {number} end - End of range (exclusive). * @param {number} [step=1] - Step between numbers. * @returns {number[]} Array of numbers in range. */ export function range(start, end, step) { if (step === void 0) { step = 1; } if (!Number.isFinite(start) || !Number.isFinite(end) || !Number.isFinite(step)) { throw new TypeError("Arguments must be finite numbers"); } if (step === 0) throw new Error("Step cannot be zero"); var isAscending = step > 0; if ((isAscending && start >= end) || (!isAscending && start <= end)) { return []; } var length = Math.max(Math.ceil((end - start) / step), 0); var result = new Array(length); for (var i = 0, value = start; i < length; i++, value += step) { result[i] = value; } return result; } /** * Returns the first n elements of an array. * * @template T The type of array elements. * @param {T[]} array - The input array. * @param {number} [n=1] - Number of elements to take. * @returns {T[]} New array with first n elements. */ export function take(array, n) { if (n === void 0) { n = 1; } if (!Array.isArray(array) || n <= 0) return []; return array.slice(0, n); } /** * Takes elements from the array while predicate returns true. * * @template T The type of array elements. * @param {T[]} array - The input array. * @param {(item: T, index: number) => boolean} predicate - Function to test each element. * @returns {T[]} New array with taken elements. */ export function takeWhile(array, predicate) { if (!Array.isArray(array)) return []; var result = []; for (var i = 0; i < array.length; i++) { if (!predicate(array[i], i)) break; result.push(array[i]); } return result; } /** * Removes all falsy values from an array. * False, null, 0, "", undefined, and NaN are falsy. * * @template T The type of array elements. * @param {T[]} array - The input array. * @returns {NonNullable<T>[]} New array with falsy values removed. */ export function compact(array) { if (!Array.isArray(array)) return []; return array.filter(Boolean); } /** * Counts array elements by a key function. * * @template T The type of array elements. * @param {T[]} array - The input array. * @param {(item: T) => string | number | symbol} keyFn - Function to generate count key. * @returns {Record<string, number>} Object with counts by key. */ export function countBy(array, keyFn) { if (!Array.isArray(array)) return {}; return array.reduce(function (acc, item) { var key = String(keyFn(item)); acc[key] = (acc[key] || 0) + 1; return acc; }, {}); } //# sourceMappingURL=array.utils.js.map