diff --git a/.changeset/great-ways-arrive.md b/.changeset/great-ways-arrive.md new file mode 100644 index 000000000..09e8eb240 --- /dev/null +++ b/.changeset/great-ways-arrive.md @@ -0,0 +1,5 @@ +--- +'@modern-kit/utils': patch +--- + +fix(utils): findKey 인터페이스 변경 - @ssi00214 diff --git a/docs/docs/utils/object/findKey.md b/docs/docs/utils/object/findKey.md index ca1de4680..6f7dcac4b 100644 --- a/docs/docs/utils/object/findKey.md +++ b/docs/docs/utils/object/findKey.md @@ -23,8 +23,12 @@ ```ts title="typescript" function findKey>( obj: T, - condition: (value: T[keyof T) => boolean -): string | undefined + predicate: (predicateData: { + value: T[keyof T]; + key: keyof T; + obj: T; + }) => boolean +): string | undefined; ``` ## Usage diff --git a/docs/docs/utils/object/mapKeys.md b/docs/docs/utils/object/mapKeys.md index a94f9156d..fdc5c7185 100644 --- a/docs/docs/utils/object/mapKeys.md +++ b/docs/docs/utils/object/mapKeys.md @@ -1,6 +1,6 @@ # mapKeys -주어진 객체의 각 키에 대해 제공된 `변환 함수`를 호출하여 새 객체를 생성하는 함수입니다. 반환된 객체는 원본 객체의 키들을 변환한 후 생성된 새로운 객체입니다. +주어진 객체의 각 키를 주어진 `iteratee` 함수 결과에 따라 변환하여 새로운 객체를 반환합니다. ## Code @@ -12,21 +12,18 @@ |이름|hz|mean|성능| |------|---|---|---| -|modern-kit/mapKeys|411,676.30|0.0024|`fastest`| -|lodash/mapKeys|386,336.12|0.0026|`slowest`| +|modern-kit/mapKeys|3,983,407.38|0.0003|`fastest`| +|lodash/mapKeys|3,060,203.94|0.0003|`slowest`| - **modern-kit/mapKeys** - - `1.07x` faster than lodash/mapKeys + - `1.30x` faster than lodash/mapKeys ## Interface ```ts title="typescript" -function mapKeys< - T extends Record, - U extends PropertyKey ->( - object: T, - iteratee: (iterateData: { key: keyof T; value: T[keyof T]; object: T }) => U -): Record +function mapKeys, K extends PropertyKey>( + obj: T, + iteratee: (iterateData: { key: keyof T; value: T[keyof T]; obj: T }) => K +): Record; ``` ## Usage diff --git a/docs/docs/utils/object/mapValues.md b/docs/docs/utils/object/mapValues.md index e55fa46d0..5500de222 100644 --- a/docs/docs/utils/object/mapValues.md +++ b/docs/docs/utils/object/mapValues.md @@ -1,6 +1,6 @@ # mapValues -주어진 객체의 각 값에 대해 제공된 `변환 함수`를 호출하여 새 객체를 생성하는 함수입니다. 반환된 객체는 원본 객체의 값들을 복사하여 변환한 후 생성된 새로운 객체입니다. +주어진 객체의 각 value를 주어진 `iteratee` 함수 결과에 따라 변환하여 새로운 객체를 반환합니다. ## Code [🔗 실제 구현 코드 확인](https://github.com/modern-agile-team/modern-kit/blob/main/packages/utils/src/object/mapValues/index.ts) @@ -11,18 +11,18 @@ |이름|hz|mean|성능| |------|---|---|---| -|modern-kit/mapValues|429,972.00|0.0023|`fastest`| -|lodash/mapValues|384,813.19|0.0026|`slowest`| +|modern-kit/mapValues|6,203,964.88|0.0003|`fastest`| +|lodash/mapValues|4,557,524.77|0.0004|`slowest`| - **modern-kit/mapValues** - - `1.12x` faster than lodash/mapValues + - `1.36x` faster than lodash/mapValues ## Interface ```ts title="typescript" function mapValues, V>( - object: T, - iteratee: (iterateData: { key: keyof T; value: T[keyof T]; object: T }) => V -): Record + obj: T, + iteratee: (iterateData: { key: keyof T; value: T[keyof T]; obj: T }) => V +): Record; ``` ## Usage diff --git a/packages/utils/src/object/findKey/findKey.bench.ts b/packages/utils/src/object/findKey/findKey.bench.ts index 0730457aa..750144f17 100644 --- a/packages/utils/src/object/findKey/findKey.bench.ts +++ b/packages/utils/src/object/findKey/findKey.bench.ts @@ -10,10 +10,10 @@ describe('findKey', () => { }; bench('modern-kit/findKey', () => { - findKey(obj, (item) => item.active); + findKey(obj, ({ value }) => value.active); }); bench('lodash/findKey', () => { - findKeyLodash(obj, (item) => item.active); + findKeyLodash(obj, (value) => value.active); }); }); diff --git a/packages/utils/src/object/findKey/findKey.spec.ts b/packages/utils/src/object/findKey/findKey.spec.ts index a790f2407..4be2683d6 100644 --- a/packages/utils/src/object/findKey/findKey.spec.ts +++ b/packages/utils/src/object/findKey/findKey.spec.ts @@ -2,23 +2,25 @@ import { describe, expect, it } from 'vitest'; import { findKey } from '.'; describe('findKey', () => { - it('should return correct key element when a existent key-value is accessed', () => { + it('predicate 함수 조건에 부합하는 첫 번째 key를 반환해야 합니다.', () => { const obj = { bike: { active: true }, plane: { active: true }, car: { active: false }, }; - expect(findKey(obj, (item) => item.active)).toEqual('bike'); + expect(findKey(obj, ({ value }) => value.active)).toEqual('bike'); }); - it('should return undefined when a non-existent key-value is accessed', () => { + it('predicate 함수 조건에 부합하는 key가 없을 경우 undefined를 반환해야 합니다.', () => { const obj = { bike: { active: true }, plane: { active: true }, car: { active: false }, }; - expect(findKey(obj, (item) => (item as any).inactive)).toEqual(undefined); + expect( + findKey(obj, ({ value }) => (value as any).inactive) + ).toBeUndefined(); }); }); diff --git a/packages/utils/src/object/findKey/index.ts b/packages/utils/src/object/findKey/index.ts index 48ebf18e9..b17b418ea 100644 --- a/packages/utils/src/object/findKey/index.ts +++ b/packages/utils/src/object/findKey/index.ts @@ -1,9 +1,16 @@ /** - * @description 객체에서 조건에 부합하는 key를 반환합니다. + * @description 객체에서 주어진 조건에 맞는 첫 번째 키를 찾습니다. + * + * 이 함수는 객체의 각 키와 값을 `predicate` 함수에 전달하여 조건을 만족하는 첫 번째 키를 반환합니다. + * 조건을 만족하는 키가 없으면 `undefined`를 반환합니다. * * @template T - 키를 찾고자 하는 객체 요소의 타입 * @param {T} obj - 검색하고자 하는 객체입니다. - * @param {(value: T[keyof T]) => boolean} condition - 검색하고자 하는 조건입니다. + * @param {(predicateData: { + * value: T[keyof T]; + * key: keyof T; + * obj: T; + * }) => boolean} predicate - 검색하고자 하는 조건을 검사하는 함수입니다. * @returns {string | undefined} 검색하고자 하는 조건에 부합하는 key를 반환합니다. 만약 조건에 부합하는 key가 없다면 undefined를 반환합니다. * * @example @@ -17,14 +24,19 @@ */ export function findKey>( obj: T, - condition: (value: T[keyof T]) => boolean, + predicate: (predicateData: { + value: T[keyof T]; + key: keyof T; + obj: T; + }) => boolean ): string | undefined { const keys = Object.keys(obj); - - for (const key of keys) { + + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; const value = obj[key]; - if (condition(value)) { + if (predicate({ value, key, obj })) { return key; } } diff --git a/packages/utils/src/object/mapKeys/index.ts b/packages/utils/src/object/mapKeys/index.ts index b4dc8b55c..3019e3728 100644 --- a/packages/utils/src/object/mapKeys/index.ts +++ b/packages/utils/src/object/mapKeys/index.ts @@ -1,30 +1,34 @@ +import { objectKeys } from '../../object/objectKeys'; + /** - * @description 주어진 객체의 각 키에 대해 제공된 변환 함수를 호출하여 새 객체를 생성하는 함수입니다. + * @description 주어진 객체의 각 key를 주어진 iteratee 함수 결과에 따라 변환하여 새로운 객체를 반환합니다. * * @template T - 원본 객체 값의 유형입니다. - * @template U - 변환된 키의 타입입니다. - * @param {T} object - 순회할 원본 객체입니다. - * @param {(iterateData: { key: keyof T; value: T[keyof T]; object: T }) => U} iteratee - 객체의 각 키에 대해 호출할 변환 함수입니다. - * @returns {Record} 변환 함수의 결과를 포함하는 새 객체를 반환합니다. + * @template K - 변환된 키의 타입입니다. + * @param {T} obj - 키를 변환할 원래 객체입니다. + * @param {(iterateData: { key: keyof T; value: T[keyof T]; obj: T }) => K} iteratee - 각 키를 변환하기 위한 함수입니다. + * @returns {Record} 변환된 key와 원래 value을 가진 새 객체를 반환합니다. * * @example * const obj = { a: 1, b: 2, c: 3 }; + * * mapKeys(obj, ({ key, value }) => key + value); * // { a1: 1, b2: 2, c3: 3 } */ export function mapKeys< T extends Record, - U extends PropertyKey + K extends PropertyKey >( - object: T, - iteratee: (iterateData: { key: keyof T; value: T[keyof T]; object: T }) => U -): Record { - const result = {} as Record; - const keys = Object.keys(object); + obj: T, + iteratee: (iterateData: { key: keyof T; value: T[keyof T]; obj: T }) => K +): Record { + const result = {} as Record; + const keys = objectKeys(obj); - for (const key of keys) { - const value = object[key] as T[keyof T]; - const newKey = iteratee({ key, value, object }); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = obj[key]; + const newKey = iteratee({ key, value, obj }); result[newKey] = value; } diff --git a/packages/utils/src/object/mapKeys/mapKeys.bench.ts b/packages/utils/src/object/mapKeys/mapKeys.bench.ts index 3d8af1f17..dbee938e4 100644 --- a/packages/utils/src/object/mapKeys/mapKeys.bench.ts +++ b/packages/utils/src/object/mapKeys/mapKeys.bench.ts @@ -2,22 +2,12 @@ import { bench, describe } from 'vitest'; import { mapKeys as mapKeysLodash } from 'lodash-es'; import { mapKeys } from '.'; -const createObj = (depth: number) => { - const obj = {} as Record; - for (let i = 0; i < depth; i++) { - obj[`key${i}`] = `value${i}`; - } - return obj; -}; - describe('mapKeys', () => { - const obj = createObj(20); - bench('modern-kit/mapKeys', () => { - mapKeys(obj, ({ key, value }) => key + value); + mapKeys({ a: 1, b: 2, c: 3, d: 4 }, ({ key, value }) => key + value); }); bench('lodash/mapKeys', () => { - mapKeysLodash(obj, (value, key) => key + value); + mapKeysLodash({ a: 1, b: 2, c: 3, d: 4 }, (value, key) => key + value); }); }); diff --git a/packages/utils/src/object/mapKeys/mapKeys.spec.ts b/packages/utils/src/object/mapKeys/mapKeys.spec.ts index 788448928..7deae3609 100644 --- a/packages/utils/src/object/mapKeys/mapKeys.spec.ts +++ b/packages/utils/src/object/mapKeys/mapKeys.spec.ts @@ -1,24 +1,35 @@ -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, expectTypeOf } from 'vitest'; import { mapKeys } from '.'; describe('mapKeys', () => { - it('should map object keys by appending values to keys', () => { + it('key와 value를 붙인 key를 가진 객체를 반환해야 합니다.', () => { const obj = { a: 1, b: 2 } as const; const expected = { a1: 1, b2: 2 } as const; const result = mapKeys(obj, ({ key, value }) => `${key}${value}`); + expect(result).toEqual(expected); + + // type + expectTypeOf(result).toEqualTypeOf< + Record<'a1' | 'a2' | 'b1' | 'b2', 1 | 2> + >(); }); - it('should map object keys to uppercase', () => { + it('대문자로 변형된 key를 가진 객체를 반환해야 합니다.', () => { const obj = { a: 'apple', b: 'banana', c: 'cherry' } as const; const expected = { A: 'apple', B: 'banana', C: 'cherry' } as const; const result = mapKeys(obj, ({ key }) => key.toString().toUpperCase()); expect(result).toEqual(expected); + + // type + expectTypeOf(result).toEqualTypeOf< + Record + >(); }); - it('should return an empty object for an empty input object', () => { + it('빈 객체의 경우 빈 객체를 반환해야 합니다.', () => { const obj = {} as const; const expected = {} as const; diff --git a/packages/utils/src/object/mapValues/index.ts b/packages/utils/src/object/mapValues/index.ts index 2479e1b36..70fe96709 100644 --- a/packages/utils/src/object/mapValues/index.ts +++ b/packages/utils/src/object/mapValues/index.ts @@ -1,27 +1,33 @@ +import { objectKeys } from '../../object/objectKeys'; + /** - * @description 주어진 객체의 각 값에 대해 제공된 변환 함수를 호출하여 새 객체를 생성하는 함수입니다. + * @description 주어진 객체의 각 value를 주어진 iteratee 함수 결과에 따라 변환하여 새로운 객체를 반환합니다. * * @template T - 원본 객체 값의 유형입니다. - * @template R - 반환할 새 객체 값의 유형입니다. - * @param {T} object - 순회할 원본 객체입니다. - * @param {(iterateData: { key: keyof T; value: T[keyof T]; object: T }) => V} iteratee - 객체의 각 값에 대해 호출할 변환 함수입니다. - * @returns {Record} 변환 함수의 결과를 포함하는 새 객체를 반환합니다. + * @template V - 반환할 새 객체 값의 유형입니다. + * @param {T} obj - 순회할 원본 객체입니다. + * @param {(iterateData: { key: keyof T; value: T[keyof T]; obj: T }) => V} iteratee - 객체의 각 값에 대해 호출할 변환 함수입니다. + * @returns {Record} 변환된 value와 원래 key를 가진 새 객체를 반환합니다. * * @example * const obj = { fred: { age: 40 }, pebbles: { age: 1 } }; + * * mapValues(users, ({ value }) => value.age) * // { fred: 40, pebbles: 1 } */ export function mapValues, V>( - object: T, - iteratee: (iterateData: { key: keyof T; value: T[keyof T]; object: T }) => V + obj: T, + iteratee: (iterateData: { key: keyof T; value: T[keyof T]; obj: T }) => V ): Record { const result = {} as Record; - const keys = Object.keys(object); + const keys = objectKeys(obj); + + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = obj[key]; + const newValue = iteratee({ key, value, obj }); - for (const key of keys) { - const value = object[key] as T[keyof T]; - result[key as keyof T] = iteratee({ key, value, object }); + result[key] = newValue; } return result; diff --git a/packages/utils/src/object/mapValues/mapValues.bench.ts b/packages/utils/src/object/mapValues/mapValues.bench.ts index ec2a0fd49..e45a60172 100644 --- a/packages/utils/src/object/mapValues/mapValues.bench.ts +++ b/packages/utils/src/object/mapValues/mapValues.bench.ts @@ -2,22 +2,12 @@ import { bench, describe } from 'vitest'; import { mapValues as mapValuesLodash } from 'lodash-es'; import { mapValues } from '.'; -const createObj = (depth: number) => { - const obj = {} as Record; - for (let i = 0; i < depth; i++) { - obj[`key${i}`] = `value${i}`; - } - return obj; -}; - describe('mapValues', () => { - const obj = createObj(30); - bench('modern-kit/mapValues', () => { - mapValues(obj, ({ value, key }) => key + value); + mapValues({ a: 1, b: 2, c: 3, d: 4 }, ({ value, key }) => key + value); }); bench('lodash/mapValues', () => { - mapValuesLodash(obj, (value, key) => key + value); + mapValuesLodash({ a: 1, b: 2, c: 3, d: 4 }, (value, key) => key + value); }); }); diff --git a/packages/utils/src/object/mapValues/mapValues.spec.ts b/packages/utils/src/object/mapValues/mapValues.spec.ts index cb614e007..71e544a32 100644 --- a/packages/utils/src/object/mapValues/mapValues.spec.ts +++ b/packages/utils/src/object/mapValues/mapValues.spec.ts @@ -1,8 +1,8 @@ -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, expectTypeOf } from 'vitest'; import { mapValues } from '.'; describe('mapValues', () => { - it('should map object values by extracting age property', () => { + it('age 값을 추출하여 이를 value로 가진 객체를 반환해야 합니다.', () => { const users = { fred: { user: 'fred', age: 40 }, pebbles: { user: 'pebbles', age: 1 }, @@ -11,19 +11,25 @@ describe('mapValues', () => { const result = mapValues(users, ({ value }) => value.age); - expect(result).toEqual(expected); + expect(mapValues(users, ({ value }) => value.age)).toEqual(expected); + + // type + expectTypeOf(result).toEqualTypeOf>(); }); - it('should map object values by transforming to uppercase string', () => { + it('대문자로 변형된 value를 가진 객체를 반환해야 합니다.', () => { const obj = { a: 'apple', b: 'banana', c: 'cherry' } as const; const expected = { a: 'APPLE', b: 'BANANA', c: 'CHERRY' } as const; const result = mapValues(obj, ({ value }) => value.toUpperCase()); expect(result).toEqual(expected); + + // type + expectTypeOf(result).toEqualTypeOf>(); }); - it('should return an empty object for an empty input object', () => { + it('빈 객체의 경우 빈 객체를 반환해야 합니다.', () => { const obj = {}; const expected = {};