Language/Javascript

Javascript - groupBy를 사용하여 조건별 그룹 나누기

둉이 2024. 7. 29. 19:00

하나의 배열을 조건에 따라 여러 개의 배열로 나누고 싶은 경우, groupBy 메소드를 사용하면 손쉽게 그룹을 나눌 수 있다.

 

groupBy2024년 6월 26일부터 ECMAScript2024(ES15)에 정식으로 채택되었으며, IE를 제외한 최신 웹 브라우저에서 지원한다.

 

"groupby" | Can I use... Support tables for HTML5, CSS3, etc

 

caniuse.com

 

 

groupBy 메소드는 Object, Map 객체를 통해 호출이 가능하며, 각각 그룹화된 Object와 Map을 반환한다.

 

실행 예시는 다음과 같다.

const arr = [1, 2, 3, 4, 5];

// 1. 배열을 조건에 따라 Object 형태로 그룹핑
const groupObj = Object.groupBy(arr, (num, i) => {
    return num % 2 ? 'odd' : 'even';
});
console.log(groupObj);  // { odd: [1, 3, 5], even: [2, 4] }

// 2. 배열을 조건에 따라 Map 형태로 그룹핑
const groupMap = Map.groupBy(arr, (num, i) => {
    return num % 2 ? 'odd' : 'even';
})
console.log(groupMap);  // { 'odd' => [1, 3, 5], 'even' => [2, 4] }

 

 

첫 번째 인자로 그룹핑할 배열(혹은 반복 가능한 객체), 두 번째 인자로 그룹 key를 리턴하는 콜백 함수를 넘기면 된다.

Object.groupBy(items, callbackFn);
// 혹은
Map.groupBy(items, callbackFn);

- items: iterable한 객체(array, string, { key: value } 형태의 object
- callbackFn: <T>(element: T, index) => string 형태의 함수

 

 

실제 개발 예시

실제 프론트엔드 개발에서는 어떤 식으로 사용하게 될까?

 

필자의 경우는 API에서 내려받은 상품 목록을 타입별 그룹으로 나눈 후, 특정 타입 순서대로 재정렬을 해주는 컨버터를 구현해야 했다.

 

따라서 Object.groupBy를 사용하여 아래와 같이 그룹화를 진행한 후, 구조 분해를 통해 그룹별 노출 순서를 조정했다.

const convertCornerHome = (
  homeData: (SuperDealMainResponse & { error: boolean }) | undefined,
) => {
  const { themeDeals = [], mainDeals = [] } = homeData || {};

  const {
    themeTimeDealItems = [],
    themeLimitedDealItems = [],
    themeRecommendedDealItems = [],
    themeUrgentDealItems = [],
  } = Object.groupBy(
    themeDeals,
    ({ themeDealType }) =>
      {
        TIME: 'themeTimeDealItems',
        LIMITED: 'themeLimitedDealItems',
        RECOMMENDED: 'themeRecommendedDealItems',
        URGENT: 'themeUrgentDealItems',
      }[themeDealType]
  );

  const totalDeals = [
    // 그룹별 노출 순서 재정렬
    ...themeRecommendedDealItems,
    ...themeUrgentDealItems,
    ...themeTimeDealItems,
    ...mainDeals.slice(0, 9),
    ...themeLimitedDealItems,
    ...mainDeals.slice(9),
  ];

  return totalDeals;
};

 

 

Node.js에서의 groupBy 사용

Node.js 환경에서 groupBy() 메소드를 호출하면 아래와 같은 TypeError가 발생할 수 있다.

TypeError: Object.groupBy is not a function

 

 

groupBy 메소드는 Node.js v21.6.* 버전에서 추가되었기 때문에 해당 함수를 사용하기 위해서는 Node 버전 21.6.* 이상을 사용해야 한다.

 

부득이하게 버전을 올릴 수 없는 경우에는 아래와 같은 방법이 있다.

 

 

방법 1 - core-js 사용

core-js를 사용하고 있다면, 해당 패키지에서 제공하는 폴리필 함수를 사용할 수 있다.

 

다만, Typescript 사용시 타입 에러가 발생할 수 있다.

import "core-js/actual/array/group-by";

const arr = [1, 2, 3, 4, 5];

const groupObj = arr.groupBy((num, i) => {
    return num % 2 ? 'odd' : 'even';
});

 

방법 2 - polyfill 함수 구현

아래 코드처럼 reduce를 사용하여 groupBy 메소드와 동일하게 동작하는 함수를 구현하는 방법도 있다.

// 1. Object.groupBy
const groupBy = <T>(arr: T[], callback: (args: T, index: number) => string) => {
  return arr.reduce<Record<string, T[]>>((obj, element, index) => {
    const key = callback(element, index);
    obj[key] = [...(obj[key] || []), element];
    return obj;
  }, {});
};

Object.groupBy ??= groupBy;  // polyfill 주입

// 2. Map.groupBy
const groupBy = <T>(arr: T[], callback: (args: T, index: number) => string) => {
  return arr.reduce<Map<string, T[]>>((map, element, index) => {
    const key = callback(element, index);
    map.set(key, [...(map.get(key) || []), element]);
    return map;
  }, new Map());
};

Map.groupBy ??= groupBy;  // polyfill 주입

 

 

참고 링크

 

TypeError: products.groupBy is not a function

I was reading that JS recently introduced a new array method groupBy to manipulate an array such const products = [ { name: 'apples', category: 'fruits' }, { name: 'oranges', category: 'fruits'...

stackoverflow.com