Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/deep-sort-sparse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cleverbrush/deep": patch
---

Reject sparse record arrays before sorting so missing elements cannot duplicate existing records or partially mutate input.
1 change: 1 addition & 0 deletions libs/deep/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,4 @@ deepSort(nodes, 1, 'position');
`RangeError` is thrown. Fields must be arrays containing finite numeric sort keys,
or a `TypeError` is thrown. All keys are checked before the array is changed.
Empty input is valid; path depth is independent of the number of records.
Sparse arrays and missing records are rejected before mutation.
9 changes: 9 additions & 0 deletions libs/deep/src/deepSort.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,12 @@ describe('deepSort', () => {
expect(() => deepSort(null as never, 0, 'path')).toThrow(TypeError);
});
});

it('rejects sparse input before sorting can duplicate an existing item', () => {
const input = new Array<{ path: number[] }>(2);
const existing = { path: [1] };
input[1] = existing;
expect(() => deepSort(input, 0, 'path')).toThrow(TypeError);
expect(0 in input).toBe(false);
expect(input[1]).toBe(existing);
});
3 changes: 2 additions & 1 deletion libs/deep/src/deepSort.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* Stably sort records in place by one numeric position in an array-valued field.
*
* Input must be a dense array of records; missing entries reject before mutation.
* All keys are validated before changing the input. An invalid index rejects
* with RangeError; missing/non-array fields and non-finite keys reject with
* TypeError. Empty input is valid. Equal keys retain their original order.
Expand All @@ -20,7 +21,7 @@ export function deepSort<T extends object>(
throw new RangeError('index must be a non-negative integer.');
}
if (!Array.isArray(items)) throw new TypeError('items must be an array.');
const sorted = items.map(item => {
const sorted = Array.from(items, item => {
const values = item?.[field];
if (!Array.isArray(values)) {
throw new TypeError('The selected field must be an array.');
Expand Down
Loading