Skip to content
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

Fix/utils #512

Merged
merged 5 commits into from
Oct 17, 2024
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/great-coins-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modern-kit/utils': patch
---

fix(utils): uniq 관련 함수 개선 - @ssi02014
5 changes: 5 additions & 0 deletions .changeset/tasty-crabs-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modern-kit/utils': minor
---

fix(utils): deepCopy -> cloneDeep 네이밍 변경 및 기능 개선 - @ssi02014
5 changes: 5 additions & 0 deletions .changeset/ten-bulldogs-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modern-kit/utils': patch
---

fix(utils): omit, pick 인터페이스 수정 및 로직 간소화 - @ssi02014
4 changes: 2 additions & 2 deletions docs/docs/utils/array/contains.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ Object.is(NaN, 0 / 0); // true
## Interface

```ts title="typescript"
const contains: <T>(
function contains<T>(
arr: T[] | readonly T[],
value: unknown,
comparator?: (x: any, y: any) => boolean
) => value is T;
): value is T;
```

## Usage
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/utils/array/excludeElements.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
```ts title="typescript"
const excludeElements: <T, U>(
arr: T[] | readonly T[],
excludeArr: T[] | readonly T[],
target: T[] | readonly T[],
iteratee?: ((item: T) => U) | undefined
) => T[];
```
Expand Down
48 changes: 48 additions & 0 deletions docs/docs/utils/common/cloneDeep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# cloneDeep

인자로 주어진 값을 `깊은 복사`를 수행하는 함수입니다.

<br />

## Code
[🔗 실제 구현 코드 확인](https://github.com/modern-agile-team/modern-kit/blob/main/packages/utils/src/common/cloneDeep/index.ts)

## Benchmark
- `hz`: 초당 작업 수
- `mean`: 평균 응답 시간(ms)

|이름|hz|mean|성능|
|------|---|---|---|
|modern-kit/cloneDeep|1,529,157.20|0.0007|`fastest`|
|lodash/cloneDeep|650,320.39|0.0015|-|

- **modern-kit/cloneDeep**
- `2.35x` faster than lodash/cloneDeep

## Interface
```ts title="typescript"
function cloneDeep<T>(value: T): T
```

## Usage
```ts title="typescript"
import { cloneDeep } from '@modern-kit/utils';
const originNum = 42;
const copyNum = cloneDeep(originNum);
const originObj = { a: 1, b: { c: 2 } };
const copyObj = cloneDeep(originObj);
const originArray = [1, 2, [3, 4]];
const copyArray = cloneDeep(originArray);
const originSet = new Set([1, 2, 3]);
const copySet = cloneDeep(originSet);
const originMap = new Map([
['a', 1],
['b', 2],
]);
const copyMap = cloneDeep(originMap);
```
36 changes: 0 additions & 36 deletions docs/docs/utils/common/deepCopy.md

This file was deleted.

18 changes: 15 additions & 3 deletions docs/docs/utils/object/omit.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,24 @@
## Code
[🔗 실제 구현 코드 확인](https://github.com/modern-agile-team/modern-kit/blob/main/packages/utils/src/object/omit/index.ts)

## Benchmark
- `hz`: 초당 작업 수
- `mean`: 평균 응답 시간(ms)

|이름|hz|mean|성능|
|------|---|---|---|
|modern-kit/omit|1,505,400.16|0.0003|`fastest`|
|lodash/omit|901,269.43|0.0011|-|

- **modern-kit/omit**
- `1.67x` faster than lodash/omit

## Interface
```ts title="typescript"
const omit: <T extends Record<PropertyKey, any>, K extends keyof T>(
function omit<T extends Record<PropertyKey, any>, K extends keyof T>(
obj: T,
keys: K[]
) => Omit<T, K>;
keys: K[] | readonly K[]
): Omit<T, K>;
```

## Usage
Expand Down
28 changes: 15 additions & 13 deletions docs/docs/utils/object/pick.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,29 @@

인자로 넣은 `keys`로 구성된 객체를 반환하는 함수입니다. 반환된 객체는 `깊은 복사된 새로운 객체`입니다.

`symbol`은 제외됩니다.

<br />

## Code
[🔗 실제 구현 코드 확인](https://github.com/modern-agile-team/modern-kit/blob/main/packages/utils/src/object/pick/index.ts)

## Benchmark
- `hz`: 초당 작업 수
- `mean`: 평균 응답 시간(ms)

|이름|hz|mean|성능|
|------|---|---|---|
|modern-kit/pick|1,693,028.73|0.0002|`fastest`|
|lodash/pick|1,022,887.39|0.0010|-|

- **modern-kit/pick**
- `1.60x` faster than lodash/pick

## Interface
```ts title="typescript"
type ObjectKeys<T extends Record<PropertyKey, T[keyof T]>> = Exclude<
keyof T,
symbol
>;

const pick: <
T extends Record<PropertyKey, T[keyof T]>,
K extends ObjectKeys<T>
>(
function pick<T extends Record<PropertyKey, any>, K extends keyof T>(
obj: T,
keys: K | K[]
) => Pick<Record<ObjectKeys<T>, T[ObjectKeys<T>]>, K>;
keys: K[] | readonly K[]
): Pick<T, K>;
```

## Usage
Expand Down
11 changes: 5 additions & 6 deletions packages/utils/src/array/contains/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
*
* @template T - 배열의 요소 타입입니다.
* @param {T[] | readonly T[]} arr - 검색할 배열입니다. 변경 불가능한 읽기 전용 배열도 허용됩니다.
* @param {U} value - 배열에서 찾고자 하는 값입니다.
* @param {(x: T, y: U) => boolean} [comparator=Object.is] - 배열의 요소와 찾고자 하는 값을 비교할 때 사용할 사용자 정의 비교 함수입니다. 기본값은 `Object.is`입니다.
* @returns {value is U} 주어진 값이 배열에 포함되어 있으면 `true`를, 그렇지 않으면 `false`를 반환합니다.
* @param {unknown} value - 배열에서 찾고자 하는 값입니다.
* @param {(x: any, y: any) => boolean} [comparator=Object.is] - 배열의 요소와 찾고자 하는 값을 비교할 때 사용할 사용자 정의 비교 함수입니다. 기본값은 `Object.is`입니다.
* @returns {value is T} 주어진 값이 배열에 포함되어 있으면 `true`를, 그렇지 않으면 `false`를 반환합니다.
*
* @example
* // 기본 비교 함수 사용 (Object.is)
Expand All @@ -18,10 +18,9 @@
* @example
* // 사용자 정의 비교 함수 사용
* const objects = [{ id: 1 }, { id: 2 }];
* const comparator = (a: { id: number }, b: { id: number }) => a.id === b.id;
*
* contains(objects, { id: 2 }, (a, b) => a.id === b.id); // true
* contains(objects, { id: 3 }, (a, b) => a.id === b.id); // false
* contains(objects, { id: 2 }, (x, y) => x.id === y.id); // true
* contains(objects, { id: 3 }, (x, y) => x.id === y.id); // false
*/
export function contains<T>(
arr: T[] | readonly T[],
Expand Down
35 changes: 30 additions & 5 deletions packages/utils/src/array/excludeElements/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
/**
* @description 주어진 배열에서 특정 요소를 제외한 배열을 반환하는 함수입니다.
* `target` 배열에 포함된 요소들이 `arr`에서 필터링되며, 선택적으로 제공되는 `iteratee` 함수를
* 사용하여 요소를 비교할 수 있습니다.
*
* @template T - 원본 배열(`arr`) 및 제외 대상 배열(`target`)의 요소 타입.
* @template U - 선택적으로 제공되는 `iteratee` 함수의 반환 타입.
*
* @param {T[] | readonly T[]} arr - 제외할 요소들을 포함한 원본 배열.
* @param {T[] | readonly T[]} target - 제외할 요소들을 포함한 배열.
* @param {(item: T) => U} [iteratee] - 요소를 비교할 때 사용하는 선택적 함수.
* 이 함수가 제공되면, 각 요소에 대해 함수의 반환값이 비교에 사용됩니다.
*
* @returns {T[]} - `target` 배열에 포함된 요소들이 제외된 원본 배열의 사본.
*
* @example
* const arr = [1, 2, 3, 4];
* const target = [2, 4];
* const result = excludeElements(arr, target);
* // 결과: [1, 3]
*
* @example
* const arr = [{ id: 1 }, { id: 2 }, { id: 3 }];
* const target = [{ id: 2 }];
* const result = excludeElements(arr, target, item => item.id);
* // 결과: [{ id: 1 }, { id: 3 }]
*/
export function excludeElements<T, U>(
arr: T[] | readonly T[],
excludeArr: T[] | readonly T[],
target: T[] | readonly T[],
iteratee?: (item: T) => U
) {
const exclusionSet = new Set<U | T>(
iteratee ? excludeArr.map(iteratee) : excludeArr
);
): T[] {
const exclusionSet = new Set<U | T>(iteratee ? target.map(iteratee) : target);

const filterFn = iteratee
? (element: T) => !exclusionSet.has(iteratee(element))
Expand Down
2 changes: 1 addition & 1 deletion packages/utils/src/array/flattenDeep/flattenDeep.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, it, expect, expectTypeOf } from 'vitest';
import { flattenDeep } from '.';

describe('flatten', () => {
describe('flattenDeep', () => {
it('should flatten a deeply nested array of numbers', () => {
const arr = [1, [2, [3, [4, [5]]]]];
const flattenedArray = flattenDeep(arr);
Expand Down
13 changes: 13 additions & 0 deletions packages/utils/src/common/cloneDeep/cloneDeep.bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { bench, describe } from 'vitest';
import { cloneDeep as cloneDeep } from '.';
import { cloneDeep as cloneDeepLodash } from 'lodash-es';

describe('cloneDeep', () => {
bench('@modern-kit/cloneDeep', () => {
cloneDeep({ a: 1, b: 2, c: { d: 4, e: [1, 2, 3] } });
});

bench('lodash/cloneDeep', () => {
cloneDeepLodash({ a: 1, b: 2, c: { d: 4, e: [1, 2, 3] } });
});
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { describe, it, expect } from 'vitest';
import { deepCopy } from '.';
import { cloneDeep } from '.';

describe('deepCopy', () => {
describe('cloneDeep', () => {
it('should deeply copy a primitive value', () => {
const originNum = 42;
const copiedNum = deepCopy(originNum);
const copiedNum = cloneDeep(originNum);

expect(copiedNum).toBe(originNum);
});

it('should deeply copy an array', () => {
const originArray = [1, 2, [3, 4]];
const copiedArray = deepCopy(originArray);
const copiedArray = cloneDeep(originArray);

expect(copiedArray).toEqual(originArray);
expect(copiedArray).not.toBe(originArray);
Expand All @@ -21,7 +21,7 @@ describe('deepCopy', () => {
const originArray: any[] = [];
originArray.push(originArray);

const copiedArr = deepCopy(originArray);
const copiedArr = cloneDeep(originArray);

expect(copiedArr).toEqual(copiedArr[0]);
expect(copiedArr).not.toBe(originArray);
Expand All @@ -32,24 +32,25 @@ describe('deepCopy', () => {
const originObject = { origin: {} };
originObject.origin = originObject;

const copiedObject = deepCopy(originObject);
const copiedObject = cloneDeep(originObject);

expect(copiedObject).toEqual(copiedObject.origin);
expect(copiedObject).not.toBe(originObject);
expect(copiedObject).not.toBe(originObject.origin);
});

it('should deeply copy an object', () => {
const originObj = { a: 1, b: { c: 2 } };
const copiedObj = deepCopy(originObj);
const originObj = { a: 1, b: { c: 2, d: [0, 1] } };
const copiedObj = cloneDeep(originObj);

expect(copiedObj).toEqual(originObj);
expect(copiedObj).not.toBe(originObj);
expect(copiedObj.b.d).not.toBe(originObj.b.d);
});

it('should deeply copy a set', () => {
const originSet = new Set([1, 2, 3]);
const copiedSet = deepCopy(originSet);
const copiedSet = cloneDeep(originSet);

expect(copiedSet).toEqual(originSet);
expect(copiedSet).not.toBe(originSet);
Expand All @@ -60,23 +61,23 @@ describe('deepCopy', () => {
['a', 1],
['b', 2],
]);
const copiedMap = deepCopy(originMap);
const copiedMap = cloneDeep(originMap);

expect(copiedMap).toEqual(originMap);
expect(copiedMap).not.toBe(originMap);
});

it('should deeply copy a date', () => {
const date = new Date();
const copiedDate = deepCopy(date);
const copiedDate = cloneDeep(date);

expect(copiedDate.getTime()).toEqual(date.getTime());
expect(copiedDate).not.toBe(date);
});

it('should deeply copy a regex', () => {
const regex = /test/gi;
const copiedRegex = deepCopy(regex);
const copiedRegex = cloneDeep(regex);

expect(copiedRegex.source).toEqual(regex.source);
expect(copiedRegex.flags).toEqual(regex.flags);
Expand Down
Loading