@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.
388 lines (383 loc) • 12 kB
JavaScript
/*
* The MIT License
*
* Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
;
var object = require('@catbee/utils/object');
var crypto = require('crypto');
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
function chunk(array, size) {
if (!Array.isArray(array)) throw new TypeError("Expected an array");
if (array.length === 0) return [];
if (!Number.isInteger(size) || size <= 0) throw new Error("Chunk size must be a positive integer");
const result = [];
for (let i = 0; i < array.length; i += size) {
result.push(array.slice(i, i + size));
}
return result;
}
__name(chunk, "chunk");
function unique(array, keyFn) {
if (!Array.isArray(array) || array.length === 0) return [];
if (!keyFn) return Array.from(new Set(array));
const seen = /* @__PURE__ */ new Set();
const result = [];
for (const item of array) {
const key = keyFn(item);
if (!seen.has(key)) {
seen.add(key);
result.push(item);
}
}
return result;
}
__name(unique, "unique");
function flattenDeep(array) {
if (!Array.isArray(array)) return [];
const result = [];
const stack = [
...array
];
while (stack.length) {
const val = stack.pop();
if (Array.isArray(val)) {
stack.push(...val);
} else {
result.push(val);
}
}
return result.reverse();
}
__name(flattenDeep, "flattenDeep");
function random(array) {
if (!Array.isArray(array) || array.length === 0) return void 0;
return array[secureIndex(array.length)];
}
__name(random, "random");
function groupBy(array, keyOrFn) {
if (!Array.isArray(array) || array.length === 0) return {};
const keyFn = typeof keyOrFn === "function" ? keyOrFn : (item) => String(object.getValueByPath(item, keyOrFn));
const result = {};
for (const item of array) {
const key = String(keyFn(item));
if (Object.hasOwn(result, key)) {
result[key].push(item);
} else {
result[key] = [
item
];
}
}
return Object.fromEntries(Object.entries(result).map(([k, v]) => [
k,
v
]));
}
__name(groupBy, "groupBy");
function shuffle(array) {
if (!Array.isArray(array)) throw new TypeError("Expected an array");
const copy = array.slice();
for (let i = copy.length - 1; i > 0; i--) {
const j = secureIndex(i + 1);
[copy[i], copy[j]] = [
copy[j],
copy[i]
];
}
return copy;
}
__name(shuffle, "shuffle");
function pluck(array, key) {
if (!Array.isArray(array)) return [];
return array.map((item) => item?.[key]);
}
__name(pluck, "pluck");
function difference(a, b) {
if (!Array.isArray(a) || !Array.isArray(b)) return [];
const setB = new Set(b);
return a.filter((item) => !setB.has(item));
}
__name(difference, "difference");
function intersect(a, b) {
if (!Array.isArray(a) || !Array.isArray(b)) return [];
const setB = new Set(b);
return a.filter((item) => setB.has(item));
}
__name(intersect, "intersect");
function mergeSort(array, key, direction = "asc", compareFn) {
if (!Array.isArray(array)) throw new TypeError("Expected array");
const arr = array.slice();
if (arr.length <= 1) return arr;
const keyFn = typeof key === "function" ? key : (item) => object.getValueByPath(item, key);
const collator = new Intl.Collator("en", {
numeric: true
});
const compare = compareFn ?? function(a, b) {
const aVal = keyFn(a);
const bVal = keyFn(b);
if (aVal === bVal) return 0;
if (aVal == null) return direction === "asc" ? 1 : -1;
if (bVal == null) return direction === "asc" ? -1 : 1;
return direction === "asc" ? collator.compare(String(aVal), String(bVal)) : collator.compare(String(bVal), String(aVal));
};
const merge = /* @__PURE__ */ __name((left, right) => {
const result = [];
let i = 0, j = 0;
while (i < left.length && j < right.length) {
result.push(compare(left[i], right[j]) <= 0 ? left[i++] : right[j++]);
}
while (i < left.length) result.push(left[i++]);
while (j < right.length) result.push(right[j++]);
return result;
}, "merge");
const sort = /* @__PURE__ */ __name((input) => {
const len = input.length;
if (len <= 1) return input;
const mid = len >> 1;
const left = sort(input.slice(0, mid));
const right = sort(input.slice(mid));
return merge(left, right);
}, "sort");
return sort(arr);
}
__name(mergeSort, "mergeSort");
function zip(...arrays) {
if (arrays.length === 0) return [];
if (arrays.some((arr) => !Array.isArray(arr))) {
throw new TypeError("All arguments must be arrays");
}
const minLength = Math.min(...arrays.map((arr) => arr.length));
const result = [];
for (let i = 0; i < minLength; i++) {
result.push(arrays.map((arr) => arr[i]));
}
return result;
}
__name(zip, "zip");
function partition(array, predicate) {
const pass = [];
const fail = [];
if (!Array.isArray(array)) return [
pass,
fail
];
for (let i = 0; i < array.length; i++) {
const item = array[i];
(predicate(item, i, array) ? pass : fail).push(item);
}
return [
pass,
fail
];
}
__name(partition, "partition");
function range(start, end, 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");
const result = [];
if (step > 0) {
for (let i = start; i < end; i += step) result.push(i);
} else {
for (let i = start; i > end; i += step) result.push(i);
}
return result;
}
__name(range, "range");
function take(array, n = 1) {
if (!Array.isArray(array) || n <= 0) return [];
return n >= array.length ? array.slice() : array.slice(0, n);
}
__name(take, "take");
function takeWhile(array, predicate) {
const result = [];
if (!Array.isArray(array)) return result;
const len = array.length;
for (let i = 0; i < len; i++) {
const item = array[i];
if (!predicate(item, i)) break;
result.push(item);
}
return result;
}
__name(takeWhile, "takeWhile");
function compact(array) {
if (!Array.isArray(array)) return [];
const result = [];
for (const v of array) {
if (v) result.push(v);
}
return result;
}
__name(compact, "compact");
function countBy(array, keyFn) {
if (!Array.isArray(array)) return {};
const result = {};
for (const item of array) {
const key = String(keyFn(item));
result[key] = (result[key] ?? 0) + 1;
}
return result;
}
__name(countBy, "countBy");
function toggle(array, item) {
if (!Array.isArray(array)) return [
item
];
const exists = array.includes(item);
if (exists) return array.filter((x) => x !== item);
return [
...array,
item
];
}
__name(toggle, "toggle");
function secureIndex(max) {
if (!Number.isInteger(max) || max <= 0) throw new RangeError("Max must be a positive integer");
const limit = 4294967295 - 4294967295 % max;
let rand;
do {
rand = crypto.randomBytes(4).readUInt32BE(0);
} while (rand >= limit);
return rand % max;
}
__name(secureIndex, "secureIndex");
var secureRandom = /* @__PURE__ */ __name((array) => {
if (!Array.isArray(array) || array.length === 0) return void 0;
const idx = secureIndex(array.length);
return array[idx];
}, "secureRandom");
function findLast(array, predicate) {
if (!Array.isArray(array)) return void 0;
for (let i = array.length - 1; i >= 0; i--) {
if (predicate(array[i], i, array)) return array[i];
}
return void 0;
}
__name(findLast, "findLast");
function findLastIndex(array, predicate) {
if (!Array.isArray(array)) return -1;
for (let i = array.length - 1; i >= 0; i--) {
if (predicate(array[i], i, array)) return i;
}
return -1;
}
__name(findLastIndex, "findLastIndex");
function chunkBy(array, predicate) {
if (!Array.isArray(array) || array.length === 0) return [];
const result = [];
let chunk2 = [];
for (let i = 0; i < array.length; i++) {
if (predicate(array[i], i, array) && chunk2.length) {
result.push(chunk2);
chunk2 = [];
}
chunk2.push(array[i]);
}
if (chunk2.length) result.push(chunk2);
return result;
}
__name(chunkBy, "chunkBy");
function remove(array, value) {
if (!Array.isArray(array)) return [];
return array.filter((item) => item !== value);
}
__name(remove, "remove");
function isSorted(array, direction = "asc", compareFn) {
if (!Array.isArray(array) || array.length <= 1) return true;
const cmp = compareFn || ((a, b) => a < b ? -1 : a > b ? 1 : 0);
for (let i = 1; i < array.length; i++) {
const res = cmp(array[i - 1], array[i]);
if (direction === "asc" && res > 0 || direction === "desc" && res < 0) return false;
}
return true;
}
__name(isSorted, "isSorted");
function headOfArr(array) {
return Array.isArray(array) && array.length > 0 ? array[0] : void 0;
}
__name(headOfArr, "headOfArr");
function lastOfArr(array) {
return Array.isArray(array) && array.length > 0 ? array.at(-1) : void 0;
}
__name(lastOfArr, "lastOfArr");
function drop(array, n) {
if (!Array.isArray(array) || n <= 0) return [
...array
];
return array.slice(n);
}
__name(drop, "drop");
function dropWhile(array, predicate) {
if (!Array.isArray(array)) return [];
let i = 0;
while (i < array.length && predicate(array[i], i)) {
i++;
}
return array.slice(i);
}
__name(dropWhile, "dropWhile");
function maxBy(array, keyOrFn) {
if (!Array.isArray(array) || array.length === 0) return void 0;
const fn = typeof keyOrFn === "function" ? keyOrFn : (item) => item[keyOrFn];
return array.reduce((max, item) => fn(item) > fn(max) ? item : max, array[0]);
}
__name(maxBy, "maxBy");
function minBy(array, keyOrFn) {
if (!Array.isArray(array) || array.length === 0) return void 0;
const fn = typeof keyOrFn === "function" ? keyOrFn : (item) => item[keyOrFn];
return array.reduce((min, item) => fn(item) < fn(min) ? item : min, array[0]);
}
__name(minBy, "minBy");
exports.chunk = chunk;
exports.chunkBy = chunkBy;
exports.compact = compact;
exports.countBy = countBy;
exports.difference = difference;
exports.drop = drop;
exports.dropWhile = dropWhile;
exports.findLast = findLast;
exports.findLastIndex = findLastIndex;
exports.flattenDeep = flattenDeep;
exports.groupBy = groupBy;
exports.headOfArr = headOfArr;
exports.intersect = intersect;
exports.isSorted = isSorted;
exports.lastOfArr = lastOfArr;
exports.maxBy = maxBy;
exports.mergeSort = mergeSort;
exports.minBy = minBy;
exports.partition = partition;
exports.pluck = pluck;
exports.random = random;
exports.range = range;
exports.remove = remove;
exports.secureIndex = secureIndex;
exports.secureRandom = secureRandom;
exports.shuffle = shuffle;
exports.take = take;
exports.takeWhile = takeWhile;
exports.toggle = toggle;
exports.unique = unique;
exports.zip = zip;