Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(utils): deepFlatThenMap 신규 유틸 함수 추가 #294

Merged
merged 13 commits into from
Jul 5, 2024

Conversation

ssi02014
Copy link
Contributor

@ssi02014 ssi02014 commented Jul 2, 2024

Overview

Issue: #289

deepFlatThenMap은 모든 깊이의 배열을 평탄화 한 후 각 요소에 콜백 함수를 적용하는 함수입니다.

반면에 lodash의 flatMapDeep 은 먼저 각 요소에 map을 적용한 후, 평탄화하는 함수로 동작상의 차이가 있습니다.

Default

const arr = [1, 2, [3, 4, [5, 6]]];
deepFlatThenMap(arr); 
// type: number[] 
// value: [1, 2, 3, 4, 5, 6]

Iteratee

const arr = [1, 2, [3, 4, [5, 6]]];
deepFlatThenMap(arr, (item) => ({ id: item }));
// type: { id: number }[]
// value: [{ id: 1}, { id: 2}, { id: 3}, { id: 4}, { id: 5}, { id: 6}];

PR Checklist

  • All tests pass.
  • All type checks pass.
  • I have read the Contributing Guide document.
    Contributing Guide

Copy link

changeset-bot bot commented Jul 2, 2024

🦋 Changeset detected

Latest commit: 668ae5a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@modern-kit/utils Minor
@modern-kit/types Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

codecov bot commented Jul 2, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 96.92%. Comparing base (cf117da) to head (668ae5a).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #294      +/-   ##
==========================================
+ Coverage   96.91%   96.92%   +0.01%     
==========================================
  Files         124      125       +1     
  Lines        1296     1301       +5     
  Branches      315      316       +1     
==========================================
+ Hits         1256     1261       +5     
  Misses         34       34              
  Partials        6        6              
Components Coverage Δ
@modern-kit/react 94.55% <ø> (ø)
@modern-kit/utils 100.00% <100.00%> (ø)

@ssi02014 ssi02014 requested a review from Sangminnn July 2, 2024 11:18
@ssi02014 ssi02014 self-assigned this Jul 2, 2024
@ssi02014 ssi02014 added feature 새로운 기능 추가 @modern-kit/utils @modern-kit/utils @modern-kit/types @modern-kit/types labels Jul 2, 2024
@ssi02014 ssi02014 changed the title feat(utils): deepFlatMap 신규 유틸 함수 추가 feat(utils): flatMapDeep 신규 유틸 함수 추가 Jul 2, 2024
Comment on lines 23 to 25
if (!iteratee) {
return arr.flat(Infinity);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iteratee가 없다면 그냥 flat을 호출합니다.

Comment on lines 27 to 36
const recursiveFlatten = (arr: T[] | readonly T[]): U[] => {
return arr.flatMap((item) => {
if (isArray(item)) {
return recursiveFlatten(item);
}
return iteratee(item as ExtractNestedArrayType<T>);
});
};

return recursiveFlatten(arr);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중첩 배열이 있다면 재귀적으로 순회하면서 배열을 평탄화합니다.

이 메서드는 map() 뒤에 깊이 1의 flat()을 붙이는 것(arr.map(...args).flat())과 동일하지만, 두 메서드를 따로 호출하는 것보다 약간 더 효율적입니다.

flatMap의 설명을 보면 map().flat()보다 flatMap이 효율적입니다. 따라서 재귀적으로 동작할 때도 flatMap을 사용합니다.

@@ -0,0 +1,37 @@
import { ExtractNestedArrayType } from '@modern-kit/types';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flatMapDeep에 대한 index파일은 jsx가 없어서 ts로 되어도 될것같습니다! 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

꼼꼼한 확인 감사합니다! 수정 반영했습니다 :)

expect(flattenArray).toEqual([1, 2, 3, 4, 5, 6]);
expectTypeOf(flattenArray).toEqualTypeOf<number[]>();
});

Copy link
Collaborator

@Sangminnn Sangminnn Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트코드 내에서 여러 타입을 가진 중첩배열에서도 ExtractNestedArrayType이 유효하게 타입을 추론해주었다는걸 검증하면 더 좋을것같다는 생각이 들었는데요!

const arr = [1, ['asdf', [true]]] 

이런 형태처럼 중첩된 배열케이스를 만들고 타입추론이 잘 되는지 확인하는 테스트코드가 추가되면 어떨까요! 😄

Copy link
Contributor Author

@ssi02014 ssi02014 Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

관련 테스트 코드도 추가했으며 동시에 ExtractNestedArrayType의 타입 테스트 코드도 함께 추가했습니다 :)

Copy link
Collaborator

@Sangminnn Sangminnn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전반적인 코드가 많이 간결해졌네요! 😂
이전 코드보다 간결해졌다보니 이해하는데에 오래걸리지 않고 기능이 직관적이라 더 좋아보입니다!
작업하시느라 너무 고생하셨습니다! 👍

@ssi02014 ssi02014 changed the title feat(utils): flatMapDeep 신규 유틸 함수 추가 feat(utils): deepFlatThenMap 신규 유틸 함수 추가 Jul 3, 2024
@ssi02014
Copy link
Contributor Author

ssi02014 commented Jul 3, 2024

@Sangminnn
몇 가지 변동 사항이 있어 말씀드립니다!!

1. 네이밍 변경

이번에 제가 제안하는 함수는 모든 깊이의 배열을 평탄화 후 각 요소에 콜백을 적용하는 함수입니다.
제가 놓쳤던 부분은 lodash의 flatMapDeep은 map을 호출 후에 평탄화 작업을 해주고 있기 때문에, 사실상 두 함수의 동작에 차이가 있습니다.


사용 예제와 비교(vs lodash flatMapDeep)

  1. iteratee가 없는 경우에는 동일하게 동작합니다.
  • Suggested deepFlatThenMap
const arr = [1, 2, [3, 4, [5, 6]]];
deepFlatThenMap(arr); 
// type: number[] 
// value: [1, 2, 3, 4, 5, 6]
  • Lodash flatMapDeep
const arr = [1, 2, [3, 4, [5, 6]]];
_.flatMapDeep(arr); 
// type: number[] 
// value: [1, 2, 3, 4, 5, 6]


  1. iteratee가 있는 경우에는 서로 동작이 다릅니다.
  • Suggested deepFlatThenMap
const arr = [1, 2, [3, 4, [5, 6]]];
deepFlatThenMap(arr, (item) => ({ id: item }));
// type: { id: number }[]
// value: [{ id: 1}, { id: 2}, { id: 3}, { id: 4}, { id: 5}, { id: 6}];
  • Lodash flatMapDeep
const arr = [1, 2, [3, 4, [5, 6]]];
_.flatMapDeep(arr, (item) => ({ id: item }));
// type: { id: number | (number | number[])[] }[];
// value: [{ id: 1}, { id: 2}, { id: [3, 4, [5, 6]]}];

위의 예제처럼 lodash의 flatMapDeep과는 동작의 차이가 있고, 이름이 같다면 혼란이 있을 수 있습니다.
따라서, 깊게 평탄화 후 map을 적용한다는 의미에서 deepFlatThenMap으로 네이밍을 변경합니다.

  • deepFlatThenMap과 flatDeepThenMap과 고민을 했는데 GPT가 전자를 추천해 전자를 선택했습니다..! (그저..갓)

2. 로직 변경

기존 재귀를 돌면서 구현했던 로직을 flat(Infinity).map(iteratee) 형태로 변경합니다. (성능을 더 고려한다면 flat보다 더 나은 선택이 있을 수 있지만 최대한 단순하게 구현하고자 하였습니다.)

벤치 마크 테스트 결과 해당 로직이 재귀를 도는 것보다 3~4배 가량 더 빠릅니다.
(이미지의 flatMapDeep 네이밍은 무시해주세요 😂)

스크린샷 2024-07-04 오전 3 05 01

@ssi02014 ssi02014 merged commit fc6eb4d into main Jul 5, 2024
3 checks passed
@ssi02014 ssi02014 deleted the feat/deepFlatMap branch July 5, 2024 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 새로운 기능 추가 @modern-kit/types @modern-kit/types @modern-kit/utils @modern-kit/utils
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants