Skip to content

Commit

Permalink
fix(utils): findKey 인터페이스 변경 (#574)
Browse files Browse the repository at this point in the history
* fix(utils): findKey 인터페이스 변경

* fix(utils): mapKeys/mapValues 개선 및 한글화
  • Loading branch information
ssi02014 authored Nov 6, 2024
1 parent ca40d96 commit 0de16eb
Show file tree
Hide file tree
Showing 13 changed files with 117 additions and 90 deletions.
5 changes: 5 additions & 0 deletions .changeset/great-ways-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modern-kit/utils': patch
---

fix(utils): findKey 인터페이스 변경 - @ssi00214
8 changes: 6 additions & 2 deletions docs/docs/utils/object/findKey.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@
```ts title="typescript"
function findKey<T extends Record<PropertyKey, any>>(
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
Expand Down
19 changes: 8 additions & 11 deletions docs/docs/utils/object/mapKeys.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# mapKeys

주어진 객체의 각 키에 대해 제공된 `변환 함수`를 호출하여 새 객체를 생성하는 함수입니다. 반환된 객체는 원본 객체의 키들을 변환한 후 생성된 새로운 객체입니다.
주어진 객체의 각 키를 주어진 `iteratee` 함수 결과에 따라 변환하여 새로운 객체를 반환합니다.

## Code

Expand All @@ -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<PropertyKey, any>,
U extends PropertyKey
>(
object: T,
iteratee: (iterateData: { key: keyof T; value: T[keyof T]; object: T }) => U
): Record<U, T[keyof T]>
function mapKeys<T extends Record<PropertyKey, any>, K extends PropertyKey>(
obj: T,
iteratee: (iterateData: { key: keyof T; value: T[keyof T]; obj: T }) => K
): Record<K, T[keyof T]>;
```

## Usage
Expand Down
14 changes: 7 additions & 7 deletions docs/docs/utils/object/mapValues.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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<T extends Record<PropertyKey, any>, V>(
object: T,
iteratee: (iterateData: { key: keyof T; value: T[keyof T]; object: T }) => V
): Record<keyof T, V>
obj: T,
iteratee: (iterateData: { key: keyof T; value: T[keyof T]; obj: T }) => V
): Record<keyof T, V>;
```

## Usage
Expand Down
4 changes: 2 additions & 2 deletions packages/utils/src/object/findKey/findKey.bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
10 changes: 6 additions & 4 deletions packages/utils/src/object/findKey/findKey.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
24 changes: 18 additions & 6 deletions packages/utils/src/object/findKey/index.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -17,14 +24,19 @@
*/
export function findKey<T extends Record<PropertyKey, any>>(
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;
}
}
Expand Down
32 changes: 18 additions & 14 deletions packages/utils/src/object/mapKeys/index.ts
Original file line number Diff line number Diff line change
@@ -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<U, T[keyof T]>} 변환 함수의 결과를 포함하는 새 객체를 반환합니다.
* @template K - 변환된 키의 타입입니다.
* @param {T} obj - 키를 변환할 원래 객체입니다.
* @param {(iterateData: { key: keyof T; value: T[keyof T]; obj: T }) => K} iteratee - 각 키를 변환하기 위한 함수입니다.
* @returns {Record<U, T[keyof T]>} 변환된 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<PropertyKey, any>,
U extends PropertyKey
K extends PropertyKey
>(
object: T,
iteratee: (iterateData: { key: keyof T; value: T[keyof T]; object: T }) => U
): Record<U, T[keyof T]> {
const result = {} as Record<U, T[keyof T]>;
const keys = Object.keys(object);
obj: T,
iteratee: (iterateData: { key: keyof T; value: T[keyof T]; obj: T }) => K
): Record<K, T[keyof T]> {
const result = {} as Record<K, T[keyof T]>;
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;
}
Expand Down
14 changes: 2 additions & 12 deletions packages/utils/src/object/mapKeys/mapKeys.bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>;
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);
});
});
19 changes: 15 additions & 4 deletions packages/utils/src/object/mapKeys/mapKeys.spec.ts
Original file line number Diff line number Diff line change
@@ -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<string, 'apple' | 'banana' | 'cherry'>
>();
});

it('should return an empty object for an empty input object', () => {
it('빈 객체의 경우 빈 객체를 반환해야 합니다.', () => {
const obj = {} as const;

const expected = {} as const;
Expand Down
28 changes: 17 additions & 11 deletions packages/utils/src/object/mapValues/index.ts
Original file line number Diff line number Diff line change
@@ -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<keyof T, V>} 변환 함수의 결과를 포함하는 새 객체를 반환합니다.
* @template V - 반환할 새 객체 값의 유형입니다.
* @param {T} obj - 순회할 원본 객체입니다.
* @param {(iterateData: { key: keyof T; value: T[keyof T]; obj: T }) => V} iteratee - 객체의 각 값에 대해 호출할 변환 함수입니다.
* @returns {Record<keyof T, V>} 변환된 value와 원래 key를 가진 새 객체를 반환합니다.
*
* @example
* const obj = { fred: { age: 40 }, pebbles: { age: 1 } };
*
* mapValues(users, ({ value }) => value.age)
* // { fred: 40, pebbles: 1 }
*/
export function mapValues<T extends Record<PropertyKey, any>, 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<keyof T, V> {
const result = {} as Record<keyof T, V>;
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;
Expand Down
14 changes: 2 additions & 12 deletions packages/utils/src/object/mapValues/mapValues.bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>;
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);
});
});
Loading

0 comments on commit 0de16eb

Please sign in to comment.