[ JS ] reduce 메서드 활용 : 같은 카테고리끼리 데이터 묶기

방충림·2023년 3월 27일
4

Code Repository

목록 보기
6/8
post-thumbnail

다음과 같은 JSON형식의 데이터가 있다.
이를 특정 용도로 활용하기 위해서 다른 형식으로 파싱해주고싶다.

기존 데이터

let mock = [
{ledger_category: '쇼핑', current_month: '2023-01', monthly_sum_count: 59000},
{ledger_category: '쇼핑', current_month: '2023-02', monthly_sum_count: 93000},
{ledger_category: '반려묘/견', current_month: '2023-03', monthly_sum_count: 35000},
{ledger_category: '병원/약국', current_month: '2023-03', monthly_sum_count: 139450},
{ledger_category: '보험비', current_month: '2023-03', monthly_sum_count: 280600},
{ledger_category: '쇼핑', current_month: '2023-03', monthly_sum_count: 555743},
{ledger_category: '식비', current_month: '2023-03', monthly_sum_count: 65000},
{ledger_category: '월급', current_month: '2023-03', monthly_sum_count: 2500000},
{ledger_category: '추가 카테고리', current_month: '2023-03', monthly_sum_count: 90000},
{ledger_category: '통신비', current_month: '2023-03', monthly_sum_count: 90700},
{ledger_category: '쇼핑', current_month: '2023-04', monthly_sum_count: 90000}]

기대 결과값

데이터를 보면 '쇼핑'카테고리가 4번 등장한다. 이 처럼 같은 카테고리를 가진 요소가 여러개 있을 경우 이것을 하나로 묶어주고자 한다.

다음과 같이 말이다.

{
  "name": "쇼핑",
  "data": [
    59000,
    93000,
    555743,
    90000
  ]
},
{
  "name": "반려묘/견",
  "data": [
    35000
  ]
},
{
  "name": "병원/약국",
  "data": [
    139450
  ]
},
{
  "name": "보험비",
  "data": [
    280600
  ]
},
{
  "name": "식비",
  "data": [
    65000
  ]
},
{
  "name": "월급",
  "data": [
    2500000
  ]
},
{
  "name": "추가 카테고리",
  "data": [
    90000
  ]
},
{
  "name": "통신비",
  "data": [
    90700
  ]

완전히 새로운 배열을 만들어 주었다.
배열 안의 객체들은 각각 name과 data라는 key들을 가지고 있고,
name에는 카테고리의 이름이, data에는 해당 카테고리에 해당됐던 요소들의 monthly_sum_count값들이 배열의 형대로 담겼다.
이러한 결과값을 만들어 주기위해 식을 어떻게 짜야할까?

로직

reduce를 이용해본다.
아래와 같이 식을 작성하면 위와같은 결과를 얻을 수 있다.

const result = mock.reduce((acc, cur) => {
  const categoryIndex = acc.findIndex(item => item.name === cur.ledger_category);
  if (categoryIndex === -1) {
    acc.push({name: cur.ledger_category, data: [cur.monthly_sum_count]});
  } else {
    acc[categoryIndex].data.push(cur.monthly_sum_count);
  }
  return acc;
}, []);


한 줄 한 줄 풀어서 순차적으로 설명해보겠다.

reduce 실행

const result = mock.reduce((acc, cur) => {

reduce라는 메서드는 두개의 파라미터(매개변수)가 있다.
어떤 변수 이름을 지정해도 상관 없지만 흔히 acc, cur로 지정한다.

각각의 의미는 다음과 같다.

  • accumulator : 누산기로서, 이전 콜백 호출에서 반환된 값 또는 reduce 메서드에 전달된 초기값.
  • currentValue : 배열의 현재 요소.

즉 배열을 cur로 한 요소씩 돌면서 acc에 축적해주는 함수이다.

(참고) 사실 reduce의 파라미터는 두개가 더 있긴하다.

  • currentIndex: 배열의 현재 인덱스. (선택 사항)
  • array: reduce가 호출된 배열. (선택 사항)

findIndex로 인덱스 찾기

  const categoryIndex = acc.findIndex(item => item.name === cur.ledger_category);

메서드 내부에서 또 하나의 메서드를 동작시킨다. "findIndex" 이것은 배열을 하나씩 돌면서 내가 찾고자하는 요소가 몇번째에 있는지를 찾아 인덱스를 반환해준다.

즉, 여기서는 축적된 배열(acc)의 요소들 중에 "name"이 현재요소(cur)의 "ledger_category"이 있는지를 확인해보고, 있으면 해당 인덱스를, 없으면 -1을 반환한다.


(예시 이미지)
결과

처음에는 당연히 acc가 빈배열 상태이므로 -1이 반환이 된다.

분기1

  if (categoryIndex === -1) {
    acc.push({name: cur.ledger_category, data: [cur.monthly_sum_count]});

-1이 반환되면 조건문에 따라 다음 코드가 실행된다.
acc배열에 {name:'쇼핑', data:'59000'}가 push 되었다.

그리고 if문이 종료되며 acc를 반환한다.

한바퀴를 돌고니 acc 는 [{name:'쇼핑', data:'59000'}]의 상태이다.

이상태에서 cur이 두번쨰 요소로 바뀐다.

분기2

  const categoryIndex = acc.findIndex(item => item.name === cur.ledger_category);

다시만난 이 친구. 카테고리는 또 쇼핑이다. 이제는 name:'쇼핑'을 가지고 있다.

그렇다면 조건문에 따라 다음 코드가 실행된다.

} else {
  acc[categoryIndex].data.push(cur.monthly_sum_count);

acc의 "쇼핑"이 있는 요소의 데이터에 "ledger_category" 값만 push해 넣는다.

과정을 끝까지 반복하면, 최종적으로 우리가 원하는 결과를 반환하게 된다.

profile
최선이 반복되면 최고가 된다.

0개의 댓글