From 184ce1778d3f8b8cde99f49fc66920fece78e569 Mon Sep 17 00:00:00 2001 From: Dongho Kim <70563791+mass2527@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:17:00 +0900 Subject: [PATCH] feat(keyBy): Add keyBy (#93) * feat(keyBy): Add keyBy * docs(keyBy): Translate title into Korean * Update docs/ko/reference/array/keyBy.md --------- Co-authored-by: Sojin Park --- benchmarks/keyBy.bench.ts | 29 ++++++++++++++++++++++++ docs/.vitepress/en.mts | 1 + docs/.vitepress/ko.mts | 1 + docs/ko/reference/array/keyBy.md | 38 ++++++++++++++++++++++++++++++++ docs/reference/array/keyBy.md | 38 ++++++++++++++++++++++++++++++++ src/array/index.ts | 1 + src/array/keyBy.spec.ts | 36 ++++++++++++++++++++++++++++++ src/array/keyBy.ts | 35 +++++++++++++++++++++++++++++ 8 files changed, 179 insertions(+) create mode 100644 benchmarks/keyBy.bench.ts create mode 100644 docs/ko/reference/array/keyBy.md create mode 100644 docs/reference/array/keyBy.md create mode 100644 src/array/keyBy.spec.ts create mode 100644 src/array/keyBy.ts diff --git a/benchmarks/keyBy.bench.ts b/benchmarks/keyBy.bench.ts new file mode 100644 index 00000000..4296936a --- /dev/null +++ b/benchmarks/keyBy.bench.ts @@ -0,0 +1,29 @@ +import { bench, describe } from 'vitest'; +import { keyBy as keyByToolkit } from 'es-toolkit'; +import { keyBy as keyByLodash } from 'lodash'; + +describe('keyBy', () => { + bench('es-toolkit/keyBy', () => { + const people = [ + { name: 'mike', age: 20 }, + { name: 'jake', age: 30 }, + { name: 'john', age: 25 }, + { name: 'sarah', age: 25 }, + { name: 'emma', age: 25 }, + ]; + + keyByToolkit(people, person => person.name); + }); + + bench('lodash/keyBy', () => { + const people = [ + { name: 'mike', age: 20 }, + { name: 'jake', age: 30 }, + { name: 'john', age: 25 }, + { name: 'sarah', age: 25 }, + { name: 'emma', age: 25 }, + ]; + + keyByLodash(people, person => person.name); + }); +}); diff --git a/docs/.vitepress/en.mts b/docs/.vitepress/en.mts index 3fb9d1b1..765ba765 100644 --- a/docs/.vitepress/en.mts +++ b/docs/.vitepress/en.mts @@ -60,6 +60,7 @@ function sidebar(): DefaultTheme.Sidebar { { text: 'intersection', link: '/reference/array/intersection' }, { text: 'intersectionBy', link: '/reference/array/intersectionBy' }, { text: 'intersectionWith', link: '/reference/array/intersectionWith' }, + { text: 'keyBy', link: '/reference/array/keyBy' }, { text: 'minBy', link: '/reference/array/minBy' }, { text: 'maxBy', link: '/reference/array/maxBy' }, { text: 'partition', link: '/reference/array/partition' }, diff --git a/docs/.vitepress/ko.mts b/docs/.vitepress/ko.mts index cceaa7d5..10716624 100644 --- a/docs/.vitepress/ko.mts +++ b/docs/.vitepress/ko.mts @@ -59,6 +59,7 @@ function sidebar(): DefaultTheme.Sidebar { { text: 'intersection', link: '/ko/reference/array/intersection' }, { text: 'intersectionBy', link: '/ko/reference/array/intersectionBy' }, { text: 'intersectionWith', link: '/ko/reference/array/intersectionWith' }, + { text: 'keyBy', link: '/ko/reference/array/keyBy' }, { text: 'minBy', link: '/ko/reference/array/minBy' }, { text: 'maxBy', link: '/ko/reference/array/maxBy' }, { text: 'partition', link: '/ko/reference/array/partition' }, diff --git a/docs/ko/reference/array/keyBy.md b/docs/ko/reference/array/keyBy.md new file mode 100644 index 00000000..0e9ce5de --- /dev/null +++ b/docs/ko/reference/array/keyBy.md @@ -0,0 +1,38 @@ +# keyBy + +배열의 요소를 쉽게 찾을 수 있도록, 키-값 쌍을 이루는 객체로 바꿔요. + +이 함수는 파라미터로 배열과 각 요소에서 키를 생성하는 함수를 받아요. +키는 함수에서 생성된 키이고, 값은 해당 키를 생성한 요소인 객체를 반환해요. +만약 동일한 키를 생성하는 요소가 여러 개 있다면, 그 중 마지막 요소가 값으로 사용돼요. + +## 인터페이스 + +```typescript +function keyBy(arr: readonly T[], getKeyFromItem: (item: T) => K): Record; +``` + +### 파라미터 + +- `arr` (`T[]`): 매핑할 배열. +- `getKeyFromItem` (`(item: T) => K`): 요소에서 키를 생성하는 함수. + +### 반환 값 + +(`Record`) 키와 해당 배열 요소들이 매핑된 객체를 반환해요. + +## 예시 + +```typescript +const array = [ + { category: 'fruit', name: 'apple' }, + { category: 'fruit', name: 'banana' }, + { category: 'vegetable', name: 'carrot' }, +]; +const result = keyBy(array, item => item.category); +// 결과값: +// { +// fruit: { category: 'fruit', name: 'banana' }, +// vegetable: { category: 'vegetable', name: 'carrot' } +// } +``` diff --git a/docs/reference/array/keyBy.md b/docs/reference/array/keyBy.md new file mode 100644 index 00000000..cbb00dde --- /dev/null +++ b/docs/reference/array/keyBy.md @@ -0,0 +1,38 @@ +# keyBy + +Maps each element of an array based on a provided key-generating function. + +This function takes an array and a function that generates a key from each element. It returns +an object where the keys are the generated keys and the values are the corresponding elements. +If there are multiple elements generating the same key, the last element among them is used as the value. + +## Signature + +```typescript +function keyBy(arr: readonly T[], getKeyFromItem: (item: T) => K): Record; +``` + +### Parameters + +- `arr` (`T[]`): The array of elements to be mapped. +- `getKeyFromItem` (`(item: T) => K`): A function that generates a key from an element. + +### Returns + +(`Record`) An object where keys are mapped to each element of an array. + +## Examples + +```typescript +const array = [ + { category: 'fruit', name: 'apple' }, + { category: 'fruit', name: 'banana' }, + { category: 'vegetable', name: 'carrot' }, +]; +const result = keyBy(array, item => item.category); +// result will be: +// { +// fruit: { category: 'fruit', name: 'banana' }, +// vegetable: { category: 'vegetable', name: 'carrot' } +// } +``` diff --git a/src/array/index.ts b/src/array/index.ts index e300bbb8..f290aaf3 100644 --- a/src/array/index.ts +++ b/src/array/index.ts @@ -11,6 +11,7 @@ export { groupBy } from './groupBy'; export { intersection } from './intersection'; export { intersectionBy } from './intersectionBy'; export { intersectionWith } from './intersectionWith'; +export { keyBy } from './keyBy'; export { maxBy } from './maxBy'; export { minBy } from './minBy'; export { partition } from './partition'; diff --git a/src/array/keyBy.spec.ts b/src/array/keyBy.spec.ts new file mode 100644 index 00000000..56891737 --- /dev/null +++ b/src/array/keyBy.spec.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest'; +import { keyBy } from './keyBy'; + +describe('keyBy', () => { + it('should return the mapped object by given key', () => { + const people = [ + { name: 'mike', age: 20 }, + { name: 'jake', age: 30 }, + ]; + + const result = keyBy(people, person => person.age.toString()); + expect(result).toEqual({ 20: { name: 'mike', age: 20 }, 30: { name: 'jake', age: 30 } }); + + const result2 = keyBy(people, person => person.name); + expect(result2).toEqual({ mike: { name: 'mike', age: 20 }, jake: { name: 'jake', age: 30 } }); + }); + + it('should overwrite the value when encountering the same key', () => { + const people = [ + { name: 'mike', age: 20 }, + { name: 'mike', age: 30 }, + ]; + + const result = keyBy(people, person => person.name); + + expect(result).toEqual({ mike: { name: 'mike', age: 30 } }); + }); + + it('should handle empty array', () => { + const people: Array<{ name: string; age: number }> = []; + + const result = keyBy(people, person => person.name); + + expect(result).toEqual({}); + }); +}); diff --git a/src/array/keyBy.ts b/src/array/keyBy.ts new file mode 100644 index 00000000..ddc3e392 --- /dev/null +++ b/src/array/keyBy.ts @@ -0,0 +1,35 @@ +/** + * Maps each element of an array based on a provided key-generating function. + * + * This function takes an array and a function that generates a key from each element. It returns + * an object where the keys are the generated keys and the values are the corresponding elements. + * If there are multiple elements generating the same key, the last element among them is used + * as the value. + * + * @param {T[]} arr - The array of elements to be mapped. + * @param {(item: T) => K} getKeyFromItem - A function that generates a key from an element. + * @returns {Record} An object where keys are mapped to each element of an array. + * + * @example + * const array = [ + * { category: 'fruit', name: 'apple' }, + * { category: 'fruit', name: 'banana' }, + * { category: 'vegetable', name: 'carrot' } + * ]; + * const result = keyBy(array, item => item.category); + * // result will be: + * // { + * // fruit: { category: 'fruit', name: 'banana' }, + * // vegetable: { category: 'vegetable', name: 'carrot' } + * // } + */ +export function keyBy(arr: readonly T[], getKeyFromItem: (item: T) => K): Record { + const result = {} as Record; + + for (const item of arr) { + const key = getKeyFromItem(item); + result[key] = item; + } + + return result; +}