From 7ad4ef15d34da98c637344879449a135ec78a53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gromit=20=28=EC=A0=84=EB=AF=BC=EC=9E=AC=29?= <64779472+ssi02014@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:01:01 +0900 Subject: [PATCH] feat(flatMapDeep): Add flatMapDeep (#464) * feat: add flatMapDeep new function * refac: flatMapDeep refactor * fix: benchmark fix * fix: benchmark fix * Apply suggestions from code review --------- Co-authored-by: Sojin Park Co-authored-by: Sojin Park --- benchmarks/performance/flatMapDeep.bench.ts | 29 +++++++++++++++++ docs/.vitepress/en.mts | 1 + docs/.vitepress/ko.mts | 1 + docs/ko/reference/array/flatMapDeep.md | 35 +++++++++++++++++++++ docs/reference/array/flatMapDeep.md | 35 +++++++++++++++++++++ package.json | 2 +- src/array/flatMapDeep.spec.ts | 20 ++++++++++++ src/array/flatMapDeep.ts | 18 +++++++++++ src/array/flattenDeep.ts | 2 +- src/array/index.ts | 1 + 10 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 benchmarks/performance/flatMapDeep.bench.ts create mode 100644 docs/ko/reference/array/flatMapDeep.md create mode 100644 docs/reference/array/flatMapDeep.md create mode 100644 src/array/flatMapDeep.spec.ts create mode 100644 src/array/flatMapDeep.ts diff --git a/benchmarks/performance/flatMapDeep.bench.ts b/benchmarks/performance/flatMapDeep.bench.ts new file mode 100644 index 00000000..12b99f06 --- /dev/null +++ b/benchmarks/performance/flatMapDeep.bench.ts @@ -0,0 +1,29 @@ +import { bench, describe } from 'vitest'; +import { flatMapDeep as flatMapDeepToolkit } from 'es-toolkit'; +import { flatMapDeep as flatMapDeepLodash } from 'lodash'; + +function createNestedArray(arr: any[], depth: number) { + let result = arr; + + for (let i = 0; i < depth; i++) { + result = [result]; + } + return result; +} + +describe('flatMapDeep', () => { + const iterateeDepth = (item: number) => createNestedArray([item, item, item], 10); + const arr = Array.from({ length: 30 }, (_, i) => i); + + bench('es-toolkit/flatMapDeep', () => { + flatMapDeepToolkit(arr, iterateeDepth); + }); + + bench('lodash/flatMapDeep', () => { + flatMapDeepLodash(arr, iterateeDepth); + }); + + bench('js built-in/map.flat', () => { + arr.map(iterateeDepth).flat(Infinity); + }); +}); diff --git a/docs/.vitepress/en.mts b/docs/.vitepress/en.mts index 30481414..f9d4718d 100644 --- a/docs/.vitepress/en.mts +++ b/docs/.vitepress/en.mts @@ -66,6 +66,7 @@ function sidebar(): DefaultTheme.Sidebar { { text: 'find (compat)', link: '/reference/compat/array/find' }, { text: 'findIndex (compat)', link: '/reference/compat/array/findIndex' }, { text: 'flatMap', link: '/reference/array/flatMap' }, + { text: 'flatMapDeep', link: '/reference/array/flatMapDeep' }, { text: 'flatten', link: '/reference/array/flatten' }, { text: 'flattenDeep', link: '/reference/array/flattenDeep' }, { text: 'forEachRight', link: '/reference/array/forEachRight' }, diff --git a/docs/.vitepress/ko.mts b/docs/.vitepress/ko.mts index 4ad73b2f..3ac3f083 100644 --- a/docs/.vitepress/ko.mts +++ b/docs/.vitepress/ko.mts @@ -71,6 +71,7 @@ function sidebar(): DefaultTheme.Sidebar { { text: 'find (호환성)', link: '/ko/reference/compat/array/find' }, { text: 'findIndex (호환성)', link: '/ko/reference/compat/array/findIndex' }, { text: 'flatMap', link: '/ko/reference/array/flatMap' }, + { text: 'flatMapDeep', link: '/ko/reference/array/flatMapDeep' }, { text: 'flatten', link: '/ko/reference/array/flatten' }, { text: 'flattenDeep', link: '/ko/reference/array/flattenDeep' }, { text: 'forEachRight', link: '/ko/reference/array/forEachRight' }, diff --git a/docs/ko/reference/array/flatMapDeep.md b/docs/ko/reference/array/flatMapDeep.md new file mode 100644 index 00000000..22d4a95c --- /dev/null +++ b/docs/ko/reference/array/flatMapDeep.md @@ -0,0 +1,35 @@ +# flatMapDeep + +중첩된 배열의 각 요소를 주어진 iteratee 함수로 매핑 후, 모든 깊이를 풀어서 평탄화해요. + +JavaScript 언어에 포함된 [Array#flat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat)을 [Array#map](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/map)과 함께 `map(iteratee).flat(Infinity)`으로 호출했을 때와 동일하게 동작하지만, 더 빨라요. + +## 인터페이스 + +```typescript +function flattenDeep(arr: T[]): Array>; +``` + +### 파라미터 + +- `arr` (`T[]`): 평탄화할 중첩 배열이에요. +- `iteratee` (`T[]`): 각 배열 요소를 매핑하는 함수에요. + +### 반환 값 + +(`Array>`): 각 요소가 매핑되고, 모든 깊이가 평탄해진 새로운 배열이에요. + +## 예시 + +```typescript +const array = [1, 2, 3]; + +const result1 = flatMapDeep(array, item => [item, item]); +// [1, 1, 2, 2, 3, 3]를 반환해요. + +const result2 = flatMapDeep(array, item => [[item, item]]); +// [1, 1, 2, 2, 3, 3]를 반환해요. + +const result3 = flatMapDeep(array, item => [[[item, item]]]); +// [1, 1, 2, 2, 3, 3]를 반환해요. +``` diff --git a/docs/reference/array/flatMapDeep.md b/docs/reference/array/flatMapDeep.md new file mode 100644 index 00000000..2940d2b0 --- /dev/null +++ b/docs/reference/array/flatMapDeep.md @@ -0,0 +1,35 @@ +# flatMapDeep + +Map each element of a nested array to the given iteratee function, then unpack and flatten all depths. + +It works the same as if you called [Array#flat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) with [Array#map](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/map) as `map(iteratee).flat(Infinity)` in the JavaScript language, but it's faster. + +## Signature + +```typescript +function flattenDeep(arr: T[]): Array>; +``` + +### Parameters + +- `arr` (`T[]`): The array to flatten. +- `iteratee` (`T[]`): A function that maps each array element. + +### Returns + +(`Array>`): A new array with each element mapped and all depths flattened. + +## Examples + +```typescript +const array = [1, 2, 3]; + +const result1 = flatMapDeep(array, item => [item, item]); +// Return [1, 1, 2, 2, 3, 3] + +const result2 = flatMapDeep(array, item => [[item, item]]); +// Return [1, 1, 2, 2, 3, 3] + +const result3 = flatMapDeep(array, item => [[[item, item]]]); +// Return [1, 1, 2, 2, 3, 3] +``` diff --git a/package.json b/package.json index 8a8cc232..9b8f14eb 100644 --- a/package.json +++ b/package.json @@ -166,4 +166,4 @@ "lint": "eslint --config eslint.config.mjs", "format": "prettier --write ." } -} \ No newline at end of file +} diff --git a/src/array/flatMapDeep.spec.ts b/src/array/flatMapDeep.spec.ts new file mode 100644 index 00000000..09538629 --- /dev/null +++ b/src/array/flatMapDeep.spec.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from 'vitest'; +import { flatMapDeep } from './flatMapDeep'; + +describe('flatMapDeep', () => { + it('should map and deeply flatten an array', () => { + const result1 = flatMapDeep([1, 2, 3], n => [[n, n]]); + expect(result1).toEqual([1, 1, 2, 2, 3, 3]); + + const result2 = flatMapDeep([1, 2, 3], n => [[[n]], [[n]]]); + expect(result2).toEqual([1, 1, 2, 2, 3, 3]); + + const result3 = flatMapDeep([1, 2, 3], n => [n, [n, [n, [n]]]]); + expect(result3).toEqual([1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]); + }); + + it('should return an empty array when provided with an empty array', () => { + const result = flatMapDeep([], n => [[n]]); + expect(result).toEqual([]); + }); +}); diff --git a/src/array/flatMapDeep.ts b/src/array/flatMapDeep.ts new file mode 100644 index 00000000..46d2d060 --- /dev/null +++ b/src/array/flatMapDeep.ts @@ -0,0 +1,18 @@ +import { ExtractNestedArrayType, flattenDeep } from './flattenDeep'; + +/** + * Recursively maps each element in an array using a provided iteratee function and then deeply flattens the resulting array. + * + * @template T - The type of elements within the array. + * @template U - The type of elements within the returned array from the iteratee function. + * @param {T[]} arr - The array to flatten. + * @param {(item: T) => U} iteratee - The function that produces the new array elements. + * @returns {Array>} A new array that has been flattened. + * + * @example + * const result = flatMapDeep([1, 2, 3], n => [[n, n]]); + * // [1, 1, 2, 2, 3, 3] + */ +export function flatMapDeep(arr: readonly T[], iteratee: (item: T) => U): Array> { + return flattenDeep(arr.map((item: T) => iteratee(item))); +} diff --git a/src/array/flattenDeep.ts b/src/array/flattenDeep.ts index e13e874e..406f6859 100644 --- a/src/array/flattenDeep.ts +++ b/src/array/flattenDeep.ts @@ -10,7 +10,7 @@ import { flatten } from './flatten.ts'; * ExtractNestedArrayType<(boolean | (string | number[])[])[]> * // string | number | boolean */ -type ExtractNestedArrayType = T extends ReadonlyArray ? ExtractNestedArrayType : T; +export type ExtractNestedArrayType = T extends ReadonlyArray ? ExtractNestedArrayType : T; /** * Flattens all depths of a nested array. diff --git a/src/array/index.ts b/src/array/index.ts index 003610cf..9a734cb0 100644 --- a/src/array/index.ts +++ b/src/array/index.ts @@ -11,6 +11,7 @@ export { dropRightWhile } from './dropRightWhile.ts'; export { dropWhile } from './dropWhile.ts'; export { fill } from './fill.ts'; export { flatMap } from './flatMap.ts'; +export { flatMapDeep } from './flatMapDeep.ts'; export { flatten } from './flatten.ts'; export { flattenDeep } from './flattenDeep.ts'; export { forEachRight } from './forEachRight.ts';