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
28 changes: 27 additions & 1 deletion src/compat/array/orderBy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,34 @@ describe('orderBy', () => {
});

it('should not include the `length` property for array-like objects', () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error - type mismatch
expect(orderBy({ 0: 3, 1: 1, 2: 2, length: 3 }, [null], ['asc'])).toEqual([1, 2, 3]);
});

it('should treat unresolved deep paths and nullish elements as `undefined` like lodash', () => {
expect(orderBy([{ a: {} }, { a: null }, null, { a: { b: 1 } }], ['a.b.c'], ['asc'])).toEqual([
{ a: {} },
{ a: null },
null,
{ a: { b: 1 } },
]);
});

it('should keep the order of nullish elements for empty or identity criteria', () => {
// empty path resolves to `undefined` like lodash's `baseGet`
expect(orderBy([null, { a: 1 }], [[]], ['asc'])).toEqual([null, { a: 1 }]);
// identity criterion keeps `null` before `undefined`
expect(orderBy([3, null, 1, undefined, 2])).toEqual([1, 2, 3, null, undefined]);
});

it('should distinguish a fully resolved `null` value from an unresolved path', () => {
// `{ a: { b: null } }` resolves `a.b` to `null`; the others are unresolved → `undefined`
expect(orderBy([{ a: { b: null } }, { a: null }, { a: {} }], ['a.b'], ['asc'])).toEqual([
{ a: { b: null } },
{ a: null },
{ a: {} },
]);
// `desc` reverses the priority order: `undefined` (higher priority) comes first
expect(orderBy([{ a: null }, { a: { b: null } }], ['a.b'], ['desc'])).toEqual([{ a: null }, { a: { b: null } }]);
});
});
13 changes: 9 additions & 4 deletions src/compat/array/orderBy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
* // { user: 'fred', age: 40 },
* // ]
*/
export function orderBy<T = any>(collection: any, criteria?: any, orders?: any, guard?: unknown): T[] {

Check warning on line 140 in src/compat/array/orderBy.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

Check warning on line 140 in src/compat/array/orderBy.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

Check warning on line 140 in src/compat/array/orderBy.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

Check warning on line 140 in src/compat/array/orderBy.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
if (collection == null) {
return [];
}
Expand All @@ -164,19 +164,24 @@

const getValueByNestedPath = (object: object, path: PropertyKey[]) => {
let target: object = object;
let index = 0;

for (let i = 0; i < path.length && target != null; ++i) {
target = target[path[i] as keyof typeof target];
for (; index < path.length && target != null; ++index) {
target = target[path[index] as keyof typeof target];
}

return target;
return index > 0 && index === path.length ? target : undefined;
};

const getValueByCriterion = (criterion: Criterion<T> | { key: PropertyKey; path: string[] }, object: T) => {
if (object == null || criterion == null) {
if (criterion == null) {
return object;
}

if (object == null) {
return undefined;
}

if (typeof criterion === 'object' && 'key' in criterion) {
if (Object.hasOwn(object, criterion.key)) {
return object[criterion.key as keyof typeof object];
Expand All @@ -197,7 +202,7 @@
};

// Prepare all cases for criteria
const preparedCriteria = criteria.map((criterion: any) => {

Check warning on line 205 in src/compat/array/orderBy.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
// lodash handles a array with one element as a single criterion
if (Array.isArray(criterion) && criterion.length === 1) {
criterion = criterion[0];
Expand All @@ -214,7 +219,7 @@
// Array.prototype.sort() always shifts the `undefined` values to the end of the array. So we have to prevent it by using a wrapper object.
const preparedCollection = (collection as T[]).map(item => ({
original: item,
criteria: preparedCriteria.map((criterion: any) => getValueByCriterion(criterion, item)),

Check warning on line 222 in src/compat/array/orderBy.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
}));

return preparedCollection
Expand Down