feat(array): add new array helpers and array-like detection#525
feat(array): add new array helpers and array-like detection#525
Conversation
nev21
commented
Feb 26, 2026
- add arrUnique, arrCompact, arrFlatten, arrGroupBy, arrChunk with ArrayLike support
- add isArrayLike helper and export in index
- add tests for new array helpers and isArrayLike
- add arrUnique, arrCompact, arrFlatten, arrGroupBy, arrChunk with ArrayLike support - add isArrayLike helper and export in index - add tests for new array helpers and isArrayLike
There was a problem hiding this comment.
Pull request overview
This pull request adds five new array helper functions with ArrayLike support and introduces the isArrayLike type guard helper. The changes enhance the library's array manipulation capabilities with common utility functions like deduplication, compaction, flattening, grouping, and chunking, all following the established arr* naming convention and supporting both arrays and array-like objects.
Changes:
- Added
isArrayLikehelper function to detect array-like objects (objects with numeric length property >= 0) - Added five new array utility functions:
arrUnique,arrCompact,arrFlatten,arrGroupBy, andarrChunk - Updated bundle size limits to accommodate the new functionality (~1KB increase)
- Removed redundant
/*#__PURE__*/markers from simple function assignments in unwrapFunction.ts - Updated version documentation in customError.ts
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| lib/src/helpers/base.ts | Adds isArrayLike type guard function with proper null/undefined/function checks and exports it |
| lib/src/array/unique.ts | New function to remove duplicate values from arrays using object-based tracking |
| lib/src/array/compact.ts | New function to filter out all falsy values from arrays |
| lib/src/array/flatten.ts | New function to flatten nested arrays to specified depth |
| lib/src/array/groupBy.ts | New function to group array elements by callback result |
| lib/src/array/chunk.ts | New function to split arrays into fixed-size chunks |
| lib/src/index.ts | Exports all new functions and types including isArrayLike |
| lib/test/src/common/helpers/base.test.ts | Comprehensive tests for isArrayLike covering various input types |
| lib/test/src/common/helpers/array.test.ts | Comprehensive tests for all five new array functions |
| lib/src/internal/unwrapFunction.ts | Removes redundant /*#__PURE__*/ markers from simple assignments |
| lib/src/helpers/customError.ts | Updates version comment from v0.12.7 to v0.13.0 for superArgsFn parameter |
| .size-limit.json | Updates bundle size limits to reflect new functionality |
| lib/test/bundle-size-check.js | Updates bundle size limits for ES5 and ES6 builds |
| README.md | Updates documentation to include new array functions in the function list |
| * Licensed under the MIT license. | ||
| */ | ||
|
|
||
| import { isArrayLike, isUndefined } from "../helpers/base"; |
There was a problem hiding this comment.
The isUndefined import is unused and should be removed to keep the code clean and reduce bundle size.
| import { isArrayLike, isUndefined } from "../helpers/base"; | |
| import { isArrayLike } from "../helpers/base"; |
| const seen: any = objCreate(null); | ||
|
|
||
| arrForEach(theArray, (item) => { | ||
| const key = normalizeJsName((typeof item) + "_" + item); |
There was a problem hiding this comment.
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.
| const keyStr = asString(callbackFn.call(thisArg, item, idx, theArray)); | ||
|
|
||
| if (!objHasOwn(result, keyStr)) { | ||
| result[keyStr] = []; | ||
| } | ||
|
|
||
| result[keyStr].push(item); |
There was a problem hiding this comment.
The callback return value is being converted to string using asString, but the function signature indicates it can return string | number | symbol. Converting symbols to strings defeats their purpose as unique identifiers and may cause unexpected behavior. Numbers are fine to convert to strings for object keys, but symbols should be handled differently.
Consider checking the type and handling symbols separately, or document that symbols will be converted to strings (which would make them lose their uniqueness property). The return type and implementation should be consistent.
| 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); |
| let chunkIdx = 0; | ||
|
|
||
| arrForEach(theArray, (item) => { | ||
| if (idx % size === 0) { | ||
| result.push([]); | ||
| chunkIdx = result.length - 1; |
There was a problem hiding this comment.
The variable chunkIdx is recalculated as result.length - 1 on every chunk creation, but it could be simplified. Since chunks are added sequentially, you could just increment chunkIdx or 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.
| let chunkIdx = 0; | |
| arrForEach(theArray, (item) => { | |
| if (idx % size === 0) { | |
| result.push([]); | |
| chunkIdx = result.length - 1; | |
| let chunkIdx = -1; | |
| arrForEach(theArray, (item) => { | |
| if (idx % size === 0) { | |
| result.push([]); | |
| chunkIdx++; |
| * 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 |
There was a problem hiding this comment.
The version number in the JSDoc comment for the superArgsFn parameter has been changed from v0.12.7 to v0.13.0. This change should be verified:
- If the parameter was actually added in v0.12.7, this change is incorrect and breaks the documentation history
- If the parameter is being added in this PR (v0.13.0), the change is correct
- If the parameter existed but is being significantly modified in v0.13.0, that should be noted differently in the documentation
Please verify the git history to confirm whether this parameter existed in v0.12.7 or is being introduced in v0.13.0.
| * @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 |