-
-
Notifications
You must be signed in to change notification settings - Fork 2
feat(array): add new array helpers and array-like detection #525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| /* | ||
| * @nevware21/ts-utils | ||
| * https://github.com/nevware21/ts-utils | ||
| * | ||
| * Copyright (c) 2026 NevWare21 Solutions LLC | ||
| * Licensed under the MIT license. | ||
| */ | ||
|
|
||
| import { isArrayLike } from "../helpers/base"; | ||
| import { arrForEach } from "./forEach"; | ||
|
|
||
| /** | ||
| * The arrChunk() method returns a new array with elements divided into groups of a specified size. | ||
| * The last group may have fewer elements if the array length is not divisible by the chunk size. | ||
| * @function | ||
| * @since 0.13.0 | ||
| * @group Array | ||
| * @group ArrayLike | ||
| * @typeParam T - Identifies the base type of array elements | ||
| * @param theArray - The array or array-like object to chunk | ||
| * @param size - The size of each chunk. Must be a positive integer | ||
| * @returns A new array of chunks, where each chunk is an array of the specified size | ||
| * @example | ||
| * ```ts | ||
| * arrChunk([1, 2, 3, 4, 5, 6, 7], 2); // [[1, 2], [3, 4], [5, 6], [7]] | ||
| * arrChunk([1, 2, 3, 4, 5], 3); // [[1, 2, 3], [4, 5]] | ||
| * arrChunk([1, 2, 3], 1); // [[1], [2], [3]] | ||
| * arrChunk([1, 2, 3], 5); // [[1, 2, 3]] | ||
| * arrChunk([], 2); // [] | ||
| * | ||
| * // Array-like objects | ||
| * arrChunk({ length: 5, 0: "a", 1: "b", 2: "c", 3: "d", 4: "e" }, 2); | ||
| * // [["a", "b"], ["c", "d"], ["e"]] | ||
| * ``` | ||
| */ | ||
| /*#__NO_SIDE_EFFECTS__*/ | ||
| export function arrChunk<T>(theArray: ArrayLike<T> | null | undefined, size: number): T[][] { | ||
| const result: T[][] = []; | ||
|
|
||
| if (isArrayLike(theArray) && size > 0) { | ||
| let idx = 0; | ||
| let chunkIdx = 0; | ||
|
|
||
| arrForEach(theArray, (item) => { | ||
| if (idx % size === 0) { | ||
| result.push([]); | ||
| chunkIdx = result.length - 1; | ||
| } | ||
|
|
||
| result[chunkIdx].push(item); | ||
| idx++; | ||
| }); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| /* | ||
| * @nevware21/ts-utils | ||
| * https://github.com/nevware21/ts-utils | ||
| * | ||
| * Copyright (c) 2026 NevWare21 Solutions LLC | ||
| * Licensed under the MIT license. | ||
| */ | ||
|
|
||
| import { isArrayLike } from "../helpers/base"; | ||
| import { arrForEach } from "./forEach"; | ||
|
|
||
| /** | ||
| * The arrCompact() method returns a new array with all falsy values removed. | ||
| * Falsy values include: false, 0, -0, 0n, "", null, undefined, and NaN. | ||
| * @function | ||
| * @since 0.13.0 | ||
| * @group Array | ||
| * @group ArrayLike | ||
| * @typeParam T - Identifies the base type of array elements | ||
| * @param theArray - The array or array-like object to compact | ||
| * @returns A new array with all falsy values filtered out | ||
| * @example | ||
| * ```ts | ||
| * arrCompact([0, 1, false, 2, "", 3, null, undefined, 4]); // [1, 2, 3, 4] | ||
| * arrCompact([false, 0, "", null, undefined]); // [] | ||
| * arrCompact([1, 2, 3]); // [1, 2, 3] | ||
| * arrCompact([]); // [] | ||
| * | ||
| * // Array-like objects | ||
| * arrCompact({ length: 5, 0: 0, 1: 1, 2: false, 3: 2, 4: null }); // [1, 2] | ||
| * ``` | ||
| */ | ||
| /*#__NO_SIDE_EFFECTS__*/ | ||
| export function arrCompact<T>(theArray: ArrayLike<T | null | undefined | false | 0 | ""> | null | undefined): T[] { | ||
| const result: T[] = []; | ||
|
|
||
| if (isArrayLike(theArray)) { | ||
| arrForEach(theArray, (item) => { | ||
| if (item) { | ||
| result.push(item); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| return result; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| /* | ||
| * @nevware21/ts-utils | ||
| * https://github.com/nevware21/ts-utils | ||
| * | ||
| * Copyright (c) 2026 NevWare21 Solutions LLC | ||
| * Licensed under the MIT license. | ||
| */ | ||
|
|
||
| import { isArray, isArrayLike, isUndefined } from "../helpers/base"; | ||
| import { arrForEach } from "./forEach"; | ||
|
|
||
| function _addItems(result: any[], arr: any, d: number): void { | ||
| const arrLen = arr.length; | ||
| let arrIdx = 0; | ||
| while (arrIdx < arrLen) { | ||
| const item = arr[arrIdx]; | ||
| if (d > 0 && isArray(item)) { | ||
| _addItems(result, item, d - 1); | ||
| } else { | ||
| result.push(item); | ||
| } | ||
| arrIdx++; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * The arrFlatten() method returns a new array with all sub-array elements flattened | ||
| * up to the specified depth (default 1). | ||
| * @function | ||
| * @since 0.13.0 | ||
| * @group Array | ||
| * @group ArrayLike | ||
| * @typeParam T - Identifies the base type of array elements | ||
| * @param theArray - The array or array-like object to flatten | ||
| * @param depth - The flattening depth, defaults to 1. Use Infinity for complete flattening | ||
| * @returns A new flattened array | ||
| * @example | ||
| * ```ts | ||
| * arrFlatten([1, [2, 3], [4, [5, 6]]]); // [1, 2, 3, 4, [5, 6]] | ||
| * arrFlatten([1, [2, 3], [4, [5, 6]]], 2); // [1, 2, 3, 4, 5, 6] | ||
| * arrFlatten([1, [2, 3], [4, [5, 6]]], Infinity); // [1, 2, 3, 4, 5, 6] | ||
| * arrFlatten([1, 2, 3]); // [1, 2, 3] | ||
| * arrFlatten([]); // [] | ||
| * | ||
| * // With array-like objects | ||
| * arrFlatten({ length: 2, 0: 1, 1: [2, 3] }); // [1, 2, 3] | ||
| * ``` | ||
| */ | ||
| /*#__NO_SIDE_EFFECTS__*/ | ||
| export function arrFlatten<T>(theArray: ArrayLike<T | any[]> | null | undefined, depth?: number): any[] { | ||
| const result: any[] = []; | ||
|
|
||
| if (isArrayLike(theArray)) { | ||
| const d = isUndefined(depth) ? 1 : depth; | ||
|
|
||
| arrForEach(theArray, (item) => { | ||
| if (d > 0 && isArray(item)) { | ||
| _addItems(result, item, d - 1); | ||
| } else { | ||
| result.push(item); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| return result; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,68 @@ | ||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||
| * @nevware21/ts-utils | ||||||||||||||||||||||||||||||||
| * https://github.com/nevware21/ts-utils | ||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||
| * Copyright (c) 2026 NevWare21 Solutions LLC | ||||||||||||||||||||||||||||||||
| * Licensed under the MIT license. | ||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import { isArrayLike, isFunction } from "../helpers/base"; | ||||||||||||||||||||||||||||||||
| import { objHasOwn } from "../object/has_own"; | ||||||||||||||||||||||||||||||||
| import { asString } from "../string/as_string"; | ||||||||||||||||||||||||||||||||
| import { arrForEach } from "./forEach"; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||
| * Callback function type for arrGroupBy | ||||||||||||||||||||||||||||||||
| * @typeParam T - Identifies the base type of array elements | ||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||
| export type ArrGroupByCallbackFn<T> = (value: T, index: number, array: ArrayLike<T>) => string | number | symbol; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||
| * The arrGroupBy() method groups array elements by the result of a callback function, | ||||||||||||||||||||||||||||||||
| * returning an object where keys are group identifiers and values are arrays of grouped elements. | ||||||||||||||||||||||||||||||||
| * @function | ||||||||||||||||||||||||||||||||
| * @since 0.13.0 | ||||||||||||||||||||||||||||||||
| * @group Array | ||||||||||||||||||||||||||||||||
| * @group ArrayLike | ||||||||||||||||||||||||||||||||
| * @typeParam T - Identifies the base type of array elements | ||||||||||||||||||||||||||||||||
| * @param theArray - The array or array-like object to group | ||||||||||||||||||||||||||||||||
| * @param callbackFn - Function that determines the group key for each element | ||||||||||||||||||||||||||||||||
| * @param thisArg - The value to use as 'this' when executing callbackFn | ||||||||||||||||||||||||||||||||
| * @returns An object with group keys as properties and arrays of grouped elements as values | ||||||||||||||||||||||||||||||||
| * @example | ||||||||||||||||||||||||||||||||
| * ```ts | ||||||||||||||||||||||||||||||||
| * const numbers = [1, 2, 3, 4, 5, 6]; | ||||||||||||||||||||||||||||||||
| * const grouped = arrGroupBy(numbers, (n) => n % 2 === 0 ? "even" : "odd"); | ||||||||||||||||||||||||||||||||
| * // { odd: [1, 3, 5], even: [2, 4, 6] } | ||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||
| * const people = [ | ||||||||||||||||||||||||||||||||
| * { name: "Alice", age: 30 }, | ||||||||||||||||||||||||||||||||
| * { name: "Bob", age: 25 }, | ||||||||||||||||||||||||||||||||
| * { name: "Charlie", age: 30 } | ||||||||||||||||||||||||||||||||
| * ]; | ||||||||||||||||||||||||||||||||
| * const byAge = arrGroupBy(people, (p) => p.age); | ||||||||||||||||||||||||||||||||
| * // { "25": [{ name: "Bob", age: 25 }], "30": [{ name: "Alice", age: 30 }, { name: "Charlie", age: 30 }] } | ||||||||||||||||||||||||||||||||
| * ``` | ||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||
| /*#__NO_SIDE_EFFECTS__*/ | ||||||||||||||||||||||||||||||||
| export function arrGroupBy<T>( | ||||||||||||||||||||||||||||||||
| theArray: ArrayLike<T> | null | undefined, | ||||||||||||||||||||||||||||||||
| callbackFn: ArrGroupByCallbackFn<T>, | ||||||||||||||||||||||||||||||||
| thisArg?: any | ||||||||||||||||||||||||||||||||
| ): Record<string | number | symbol, T[]> { | ||||||||||||||||||||||||||||||||
| const result: Record<string | number | symbol, T[]> = {}; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (isArrayLike(theArray) && isFunction(callbackFn)) { | ||||||||||||||||||||||||||||||||
| arrForEach(theArray, (item, idx) => { | ||||||||||||||||||||||||||||||||
| const keyStr = asString(callbackFn.call(thisArg, item, idx, theArray)); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (!objHasOwn(result, keyStr)) { | ||||||||||||||||||||||||||||||||
| result[keyStr] = []; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| result[keyStr].push(item); | ||||||||||||||||||||||||||||||||
|
Comment on lines
+57
to
+63
|
||||||||||||||||||||||||||||||||
| const keyStr = asString(callbackFn.call(thisArg, item, idx, theArray)); | |
| if (!objHasOwn(result, keyStr)) { | |
| result[keyStr] = []; | |
| } | |
| result[keyStr].push(item); | |
| const rawKey = callbackFn.call(thisArg, item, idx, theArray); | |
| const key: string | number | symbol = typeof rawKey === "symbol" ? rawKey : asString(rawKey); | |
| if (!objHasOwn(result, key)) { | |
| result[key] = []; | |
| } | |
| result[key].push(item); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,55 @@ | ||||||
| /* | ||||||
| * @nevware21/ts-utils | ||||||
| * https://github.com/nevware21/ts-utils | ||||||
| * | ||||||
| * Copyright (c) 2026 NevWare21 Solutions LLC | ||||||
| * Licensed under the MIT license. | ||||||
| */ | ||||||
|
|
||||||
| import { isArrayLike, isUndefined } from "../helpers/base"; | ||||||
|
||||||
| import { isArrayLike, isUndefined } from "../helpers/base"; | |
| import { isArrayLike } from "../helpers/base"; |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using normalizeJsName for deduplication keys is problematic. The normalizeJsName function is designed to convert strings into valid JavaScript identifiers by replacing invalid characters with underscores, not for creating unique deduplication keys.
This implementation has potential issues:
- It's unnecessary overhead - the function performs regex replacement meant for identifier normalization
- It could theoretically cause key collisions if values contain characters that get normalized (e.g., spaces, dots, special chars)
- The approach works for primitives but would silently fail for objects (all objects would map to "object_[object Object]")
A simpler and more reliable approach would be to use the value directly as the key with type prefix, since JavaScript object keys are already strings:
const key = (typeof item) + ":" + item;This avoids the normalization overhead and is clearer in intent. Alternatively, for ES6+ environments, consider using a Set or Map for better performance and correctness.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -55,7 +55,7 @@ function _setName(baseClass: any, name: string) { | |||||
| * @param constructCb - [Optional] An optional callback function to call when a | ||||||
| * new Custom Error instance is being created. | ||||||
| * @param errorBase - [Optional] (since v0.9.6) The error class to extend for this class, defaults to Error. | ||||||
| * @param superArgsFn - [Optional] (since v0.12.7) An optional function that receives the constructor arguments and | ||||||
| * @param superArgsFn - [Optional] (since v0.13.0) An optional function that receives the constructor arguments and | ||||||
|
||||||
| * @param superArgsFn - [Optional] (since v0.13.0) An optional function that receives the constructor arguments and | |
| * @param superArgsFn - [Optional] (since v0.12.7) An optional function that receives the constructor arguments and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable
chunkIdxis recalculated asresult.length - 1on every chunk creation, but it could be simplified. Since chunks are added sequentially, you could just incrementchunkIdxor track it more efficiently. However, the current implementation is clear and the performance impact is negligible, so this is a very minor optimization opportunity rather than a bug.