feat(sortBy): implement es-toolkit/sortBy (#381)

* Implement sortBy

* Add bench

* Extends template from object

* Fix example

* Add mixed iteratees

* Add docs and fix examples

* Fix jsdoc

* make prettier

* Update src/array/sortBy.ts

---------

Co-authored-by: Sojin Park <raon0211@gmail.com>
This commit is contained in:
Dayong Lee 2024-08-15 11:15:35 +09:00 committed by GitHub
parent 8310cd9d5d
commit 21a0ceabc5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 240 additions and 0 deletions

View File

@ -0,0 +1,22 @@
import { bench, describe } from 'vitest';
import { sortBy as sortByToolkit } from 'es-toolkit';
import { sortBy as sortByLodash } from 'lodash';
describe('sortBy', () => {
const users = [
{ user: 'fred', age: 48, nested: { user: 'fred' } },
{ user: 'barney', age: 34, nested: { user: 'barney' } },
{ user: 'fred', age: 40, nested: { user: 'fred' } },
{ user: 'barney', age: 36, nested: { user: 'bar' } },
];
const keys: Array<keyof (typeof users)[0]> = ['user', 'age'];
bench('es-toolkit/sortBy', () => {
sortByToolkit(users, keys);
sortByToolkit(users, [user => user.user, user => user.age]);
});
bench('lodash/sortBy', () => {
sortByLodash(users, keys);
sortByLodash(users, [user => user.user, user => user.age]);
});
});

View File

@ -87,6 +87,7 @@ function sidebar(): DefaultTheme.Sidebar {
{ text: 'sampleSize', link: '/reference/array/sampleSize' },
{ text: 'shuffle', link: '/reference/array/shuffle' },
{ text: 'size (compat)', link: '/reference/compat/array/size' },
{ text: 'sortBy', link: '/reference/array/sortBy' },
{ text: 'take', link: '/reference/array/take' },
{ text: 'takeWhile', link: '/reference/array/takeWhile' },
{ text: 'takeRight', link: '/reference/array/takeRight' },

View File

@ -95,6 +95,7 @@ function sidebar(): DefaultTheme.Sidebar {
{ text: 'sampleSize', link: '/ko/reference/array/sampleSize' },
{ text: 'shuffle', link: '/ko/reference/array/shuffle' },
{ text: 'size (호환성)', link: '/ko/reference/compat/array/size' },
{ text: 'sortBy', link: '/ko/reference/array/sortBy' },
{ text: 'take', link: '/ko/reference/array/take' },
{ text: 'takeWhile', link: '/ko/reference/array/takeWhile' },
{ text: 'takeRight', link: '/ko/reference/array/takeRight' },

View File

@ -0,0 +1,47 @@
# sortBy
주어진 `iteratees` (또는 키)에 따라 객체 배열을 오름차순으로 정렬해요.
이 함수는 객체 배열, 정렬할 기준이 되는 iteratee (또는 키)의 배열을 받아요.
오름차순으로 정렬된 객체 배열을 반환해요.
`iteratees`가 객체의 키일 경우엔 해당 키에 해당하는 값을 기준으로 정렬해요.
`iteratees``iteratee` 함수일 경우엔 해당 함수의 반환값을 기준으로 정렬해요.
키의 값이 동일한 경우 다음 키를 기준으로 정렬 순서를 결정해요.
> `iteratee` 함수는 객체를 매개변수로 받고 값을 반환하는 함수에요.
## 인터페이스
```typescript
function sortBy<T extends object>(collection: T[], iteratees: Array<Iteratee<T> | keyof T>): T[];
```
### 파라미터
- `collection` (`T[]`): 정렬할 객체 배열.
- `iteratees` (`Array<Iteratee<T> | keyof T>`): 정렬할 기준이 되는 iteratee 또는 키의 배열.
### 반환 값
(`T[]`) 정렬된 배열.
## 예시
```typescript
const users = [
{ user: 'foo', age: 24 },
{ user: 'bar', age: 7 },
{ user: 'foo ', age: 8 },
{ user: 'bar ', age: 29 },
];
sortBy(users, ['user', 'age']);
sortBy(users, [obj => obj.user, 'age']);
// results will be:
// [
// { user : 'bar', age: 7 },
// { user : 'bar', age: 29 },
// { user : 'foo', age: 8 },
// { user : 'foo', age: 24 },
// ]
```

View File

@ -0,0 +1,47 @@
# sortBy
Sorts an array of objects based on the given `iteratees` (or keys) in ascending order.
This function takes an array of objects, an array of iteratees (or keys) to sort by.
It returns the ascendingly sorted array of objects.
If `iteratees` are keys of the object, it sorts based on the values of the keys.
If `iteratees` are iteratee functions, it sorts based on the return values of the functions.
If values for a key are equal, it moves to the next key to determine the order.
> An `iteratee` is a function that takes an object and returns a value.
## Signature
```typescript
function sortBy<T extends object>(collection: T[], iteratees: Array<Iteratee<T>> | Array<keyof T>): T[];
```
### Parameters
- `collection` (`T[]`): The array of objects to be sorted.
- `iteratees` (`Array<Iteratee<T>> | Array<keyof T>`): The array of iteratees or keys to sort by.
### Returns
(`T[]`) The ascendingly sorted array of objects.
## Examples
```typescript
const users = [
{ user: 'foo', age: 24 },
{ user: 'bar', age: 7 },
{ user: 'foo ', age: 8 },
{ user: 'bar ', age: 29 },
];
sortBy(users, ['user', 'age']);
sortBy(users, [obj => obj.user, obj => obj.age]);
// results will be:
// [
// { user : 'bar', age: 7 },
// { user : 'bar', age: 29 },
// { user : 'foo', age: 8 },
// { user : 'foo', age: 24 },
// ]
```

View File

@ -22,6 +22,7 @@ export { maxBy } from './maxBy.ts';
export { minBy } from './minBy.ts';
export { orderBy } from './orderBy.ts';
export { partition } from './partition.ts';
export { sortBy } from './sortBy.ts';
export { sample } from './sample.ts';
export { sampleSize } from './sampleSize.ts';
export { shuffle } from './shuffle.ts';

56
src/array/sortBy.spec.ts Normal file
View File

@ -0,0 +1,56 @@
import { describe, it, expect } from 'vitest';
import { sortBy } from './sortBy';
describe('sortBy', () => {
const users = [
{ user: 'foo', age: 24 },
{ user: 'bar', age: 7 },
{ user: 'foo', age: 8 },
{ user: 'bar', age: 29 },
];
it('should stable sort objects by a single property in ascending order', () => {
expect(sortBy(users, ['user'])).toEqual([
{ user: 'bar', age: 7 },
{ user: 'bar', age: 29 },
{ user: 'foo', age: 24 },
{ user: 'foo', age: 8 },
]);
});
it('should stable sort objects by multiple properties', () => {
expect(sortBy(users, ['user', 'age'])).toEqual([
{ user: 'bar', age: 7 },
{ user: 'bar', age: 29 },
{ user: 'foo', age: 8 },
{ user: 'foo', age: 24 },
]);
});
it('should stable sort objects by iteratee function', () => {
expect(sortBy(users, [user => user.user])).toEqual([
{ user: 'bar', age: 7 },
{ user: 'bar', age: 29 },
{ user: 'foo', age: 24 },
{ user: 'foo', age: 8 },
]);
});
it('should stable sort objects by iteratee function and property', () => {
expect(sortBy(users, [user => user.user, user => user.age])).toEqual([
{ user: 'bar', age: 7 },
{ user: 'bar', age: 29 },
{ user: 'foo', age: 8 },
{ user: 'foo', age: 24 },
]);
});
it('should stable sort objects by mixed iteratee function and key', () => {
expect(sortBy(users, ['user', user => user.age])).toEqual([
{ user: 'bar', age: 7 },
{ user: 'bar', age: 29 },
{ user: 'foo', age: 8 },
{ user: 'foo', age: 24 },
]);
});
});

65
src/array/sortBy.ts Normal file
View File

@ -0,0 +1,65 @@
type Iteratee<T> = (object: T) => T[keyof T];
/**
* Sorts an array of objects based on the given iteratees or keys in ascending order.
*
* This function takes an array of objects, an array of iteratees (or keys) to sort by.
* It returns the ascendingly sorted array of objects.
* If `iteratees` are keys of the object, it sorts based on the values of the keys.
* If `iteratees` are iteratee functions, it sorts based on the return values of the functions.
* If values for a key are equal, it moves to the next key to determine the order.
*
* @template T - The type of the objects in the array.
* @param {T[]} collection - The array of objects to be sorted.
* @param {Array<Iteratee<T> | keyof T>} iteratees - The array of iteratees or keys to sort by.
* @returns {T[]} The ascendingly sorted array of objects.
*
* @example
* const users = [
* { user: 'foo', age: 24 },
* { user: 'bar', age: 7 },
* { user: 'foo ', age: 8 },
* { user: 'bar ', age: 29 },
* ];
*
* sortBy(users, ['user', 'age']);
* sortBy(users, [obj => obj.user, 'age']);
* // results will be:
* // [
* // { user : 'bar', age: 7 },
* // { user : 'bar', age: 29 },
* // { user : 'foo', age: 8 },
* // { user : 'foo', age: 24 },
* // ]
*/
export function sortBy<T extends object>(collection: T[], iteratees: Array<Iteratee<T> | keyof T>): T[] {
const compareValues = (a: T[keyof T], b: T[keyof T]) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
};
return collection.slice().sort((a, b) => {
for (let i = 0; i < iteratees.length; i++) {
const iteratee = iteratees[i];
const iterateeIsFunction = typeof iteratee === 'function';
const valueA = iterateeIsFunction ? iteratee(a) : a[iteratee];
const valueB = iterateeIsFunction ? iteratee(b) : b[iteratee];
const result = compareValues(valueA, valueB);
if (result !== 0) {
return result;
}
}
return 0;
});
}