From a267fda528961aaeae86d4a7eac4d07cb541cb5d Mon Sep 17 00:00:00 2001 From: ssi02014 Date: Wed, 16 Oct 2024 18:25:13 +0900 Subject: [PATCH] =?UTF-8?q?fix(utils):=20deepCopy=20->=20cloneDeep=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/tasty-crabs-work.md | 5 ++ docs/docs/utils/common/cloneDeep.md | 48 +++++++++++++++++++ docs/docs/utils/common/deepCopy.md | 36 -------------- docs/docs/utils/object/omit.md | 6 +-- docs/docs/utils/object/pick.md | 8 ++-- .../src/common/cloneDeep/cloneDeep.bench.ts | 13 +++++ .../cloneDeep.spec.ts} | 25 +++++----- .../common/{deepCopy => cloneDeep}/index.ts | 27 ++++++----- packages/utils/src/common/index.ts | 2 +- .../utils/src/common/wrapInArray/index.ts | 4 +- packages/utils/src/object/omit/index.ts | 4 +- packages/utils/src/object/pick/index.ts | 4 +- 12 files changed, 109 insertions(+), 73 deletions(-) create mode 100644 .changeset/tasty-crabs-work.md create mode 100644 docs/docs/utils/common/cloneDeep.md delete mode 100644 docs/docs/utils/common/deepCopy.md create mode 100644 packages/utils/src/common/cloneDeep/cloneDeep.bench.ts rename packages/utils/src/common/{deepCopy/deepCopy.spec.ts => cloneDeep/cloneDeep.spec.ts} (76%) rename packages/utils/src/common/{deepCopy => cloneDeep}/index.ts (75%) diff --git a/.changeset/tasty-crabs-work.md b/.changeset/tasty-crabs-work.md new file mode 100644 index 000000000..10ec8be36 --- /dev/null +++ b/.changeset/tasty-crabs-work.md @@ -0,0 +1,5 @@ +--- +'@modern-kit/utils': minor +--- + +fix(utils): deepCopy -> cloneDeep 네이밍 변경 및 기능 개선 - @ssi02014 diff --git a/docs/docs/utils/common/cloneDeep.md b/docs/docs/utils/common/cloneDeep.md new file mode 100644 index 000000000..7e5023d1f --- /dev/null +++ b/docs/docs/utils/common/cloneDeep.md @@ -0,0 +1,48 @@ +# cloneDeep + +인자로 주어진 값을 `깊은 복사`를 수행하는 함수입니다. + +
+ +## 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(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); +``` \ No newline at end of file diff --git a/docs/docs/utils/common/deepCopy.md b/docs/docs/utils/common/deepCopy.md deleted file mode 100644 index d9f3ce61b..000000000 --- a/docs/docs/utils/common/deepCopy.md +++ /dev/null @@ -1,36 +0,0 @@ -# deepCopy - -인자로 주어진 값을 `깊은 복사`를 수행하는 함수입니다. - -
- -## Code -[🔗 실제 구현 코드 확인](https://github.com/modern-agile-team/modern-kit/blob/main/packages/utils/src/common/deepCopy/index.ts) - -## Interface -```ts title="typescript" -const deepCopy: (value: T) => T -``` - -## Usage -```ts title="typescript" -import { deepCopy } from '@modern-kit/utils'; - -const originNum = 42; -const copyNum = deepCopy(originNum); - -const originObj = { a: 1, b: { c: 2 } }; -const copyObj = deepCopy(originObj); - -const originArray = [1, 2, [3, 4]]; -const copyArray = deepCopy(originArray); - -const originSet = new Set([1, 2, 3]); -const copySet = deepCopy(originSet); - -const originMap = new Map([ - ['a', 1], - ['b', 2], -]); -const copyMap = deepCopy(originMap); -``` \ No newline at end of file diff --git a/docs/docs/utils/object/omit.md b/docs/docs/utils/object/omit.md index 28cdf16a9..2d69283f8 100644 --- a/docs/docs/utils/object/omit.md +++ b/docs/docs/utils/object/omit.md @@ -15,11 +15,11 @@ |이름|hz|mean|성능| |------|---|---|---| -|modern-kit/omit|3,567,312.89|0.0003|`fastest`| -|lodash/omit|879,967.75|0.0011|-| +|modern-kit/omit|1,505,400.16|0.0003|`fastest`| +|lodash/omit|901,269.43|0.0011|-| - **modern-kit/omit** - - `4.05x` faster than lodash/omit + - `1.67x` faster than lodash/omit ## Interface ```ts title="typescript" diff --git a/docs/docs/utils/object/pick.md b/docs/docs/utils/object/pick.md index 3acb72300..758853ba5 100644 --- a/docs/docs/utils/object/pick.md +++ b/docs/docs/utils/object/pick.md @@ -2,8 +2,6 @@ 인자로 넣은 `keys`로 구성된 객체를 반환하는 함수입니다. 반환된 객체는 `깊은 복사된 새로운 객체`입니다. -`symbol`은 제외됩니다. -
## Code @@ -15,11 +13,11 @@ |이름|hz|mean|성능| |------|---|---|---| -|modern-kit/pick|5,663,602.36|0.0002|`fastest`| -|lodash/pick|1,035,576.42|0.0010|-| +|modern-kit/pick|1,693,028.73|0.0002|`fastest`| +|lodash/pick|1,022,887.39|0.0010|-| - **modern-kit/pick** - - `4.05x` faster than lodash/pick + - `1.60x` faster than lodash/pick ## Interface ```ts title="typescript" diff --git a/packages/utils/src/common/cloneDeep/cloneDeep.bench.ts b/packages/utils/src/common/cloneDeep/cloneDeep.bench.ts new file mode 100644 index 000000000..7f9c1f792 --- /dev/null +++ b/packages/utils/src/common/cloneDeep/cloneDeep.bench.ts @@ -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] } }); + }); +}); diff --git a/packages/utils/src/common/deepCopy/deepCopy.spec.ts b/packages/utils/src/common/cloneDeep/cloneDeep.spec.ts similarity index 76% rename from packages/utils/src/common/deepCopy/deepCopy.spec.ts rename to packages/utils/src/common/cloneDeep/cloneDeep.spec.ts index ee5d045d4..796c72e61 100644 --- a/packages/utils/src/common/deepCopy/deepCopy.spec.ts +++ b/packages/utils/src/common/cloneDeep/cloneDeep.spec.ts @@ -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); @@ -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); @@ -32,7 +32,7 @@ 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); @@ -40,16 +40,17 @@ describe('deepCopy', () => { }); 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); @@ -60,7 +61,7 @@ describe('deepCopy', () => { ['a', 1], ['b', 2], ]); - const copiedMap = deepCopy(originMap); + const copiedMap = cloneDeep(originMap); expect(copiedMap).toEqual(originMap); expect(copiedMap).not.toBe(originMap); @@ -68,7 +69,7 @@ describe('deepCopy', () => { 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); @@ -76,7 +77,7 @@ describe('deepCopy', () => { 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); diff --git a/packages/utils/src/common/deepCopy/index.ts b/packages/utils/src/common/cloneDeep/index.ts similarity index 75% rename from packages/utils/src/common/deepCopy/index.ts rename to packages/utils/src/common/cloneDeep/index.ts index 0632c0f9a..837d769b5 100644 --- a/packages/utils/src/common/deepCopy/index.ts +++ b/packages/utils/src/common/cloneDeep/index.ts @@ -1,4 +1,4 @@ -export function deepCopy(value: T) { +export function cloneDeep(value: T) { const referenceMap = new WeakMap(); const copyWthRecursion = (target: T): T => { @@ -14,11 +14,11 @@ export function deepCopy(value: T) { // Array if (Array.isArray(target)) { - const newArray: any[] = []; - + const newArray = new Array(target.length); referenceMap.set(target, newArray); - for (const item of target) { - newArray.push(copyWthRecursion(item)); + + for (let i = 0; i < target.length; i++) { + newArray[i] = copyWthRecursion(target[i]); } return newArray as T; } @@ -26,8 +26,8 @@ export function deepCopy(value: T) { // Set if (target instanceof Set) { const newSet = new Set(); - referenceMap.set(target, newSet); + for (const item of target) { newSet.add(copyWthRecursion(item)); } @@ -52,18 +52,21 @@ export function deepCopy(value: T) { // RegExp if (target instanceof RegExp) { - return new RegExp(target.source, target.flags) as T; + const result = new RegExp(target.source, target.flags); + result.lastIndex = target.lastIndex; + + return result as T; } // Object - const newObject: Record = Object.create( - Object.getPrototypeOf(target) - ); - + const newObject: Record = {}; referenceMap.set(target, newObject); + const keys = Reflect.ownKeys(target); // symbol 유지 - for (const key of keys) { + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + newObject[key] = copyWthRecursion( (target as Record)[key] ); diff --git a/packages/utils/src/common/index.ts b/packages/utils/src/common/index.ts index 5209ce5a0..15594ef2a 100644 --- a/packages/utils/src/common/index.ts +++ b/packages/utils/src/common/index.ts @@ -1,6 +1,6 @@ export * from './abRandom'; export * from './asyncNoop'; -export * from './deepCopy'; +export * from './cloneDeep'; export * from './delay'; export * from './getUniqId'; export * from './getUniqTime'; diff --git a/packages/utils/src/common/wrapInArray/index.ts b/packages/utils/src/common/wrapInArray/index.ts index c1778ef9f..faec0c547 100644 --- a/packages/utils/src/common/wrapInArray/index.ts +++ b/packages/utils/src/common/wrapInArray/index.ts @@ -1,7 +1,7 @@ -import { deepCopy } from '../deepCopy'; +import { cloneDeep } from '../cloneDeep'; export function wrapInArray(value: T | T[]): T[] { - const copiedValue = deepCopy(value); + const copiedValue = cloneDeep(value); return Array.isArray(copiedValue) ? copiedValue : [copiedValue]; } diff --git a/packages/utils/src/object/omit/index.ts b/packages/utils/src/object/omit/index.ts index a71af6099..b4420f828 100644 --- a/packages/utils/src/object/omit/index.ts +++ b/packages/utils/src/object/omit/index.ts @@ -1,3 +1,5 @@ +import { cloneDeep } from '../../common'; + /** * @description 주어진 객체에서 지정된 키들을 제거한 새로운 객체를 반환하는 함수입니다. * @@ -17,7 +19,7 @@ export function omit, K extends keyof T>( obj: T, keys: K[] | readonly K[] ): Omit { - const result = { ...obj }; + const result = cloneDeep(obj); for (let i = 0; i < keys.length; i++) { const key = keys[i]; diff --git a/packages/utils/src/object/pick/index.ts b/packages/utils/src/object/pick/index.ts index 670641eba..af814d08b 100644 --- a/packages/utils/src/object/pick/index.ts +++ b/packages/utils/src/object/pick/index.ts @@ -1,3 +1,5 @@ +import { cloneDeep } from '../../common'; + /** * @description 주어진 객체에서 지정된 키들만을 선택하여 새로운 객체를 반환하는 함수입니다. * @@ -23,7 +25,7 @@ export function pick, K extends keyof T>( keys: K[] | readonly K[] ): Pick { const result = {} as T; - const copiedObj = { ...obj }; + const copiedObj = cloneDeep(obj); for (let i = 0; i < keys.length; i++) { const key = keys[i];