다음과 같은 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;
}, []);
const result = mock.reduce((acc, cur) => {
reduce라는 메서드는 두개의 파라미터(매개변수)가 있다.
어떤 변수 이름을 지정해도 상관 없지만 흔히 acc, cur로 지정한다.
각각의 의미는 다음과 같다.
- accumulator : 누산기로서, 이전 콜백 호출에서 반환된 값 또는 reduce 메서드에 전달된 초기값.
- currentValue : 배열의 현재 요소.
즉 배열을 cur로 한 요소씩 돌면서 acc에 축적해주는 함수이다.
(참고) 사실 reduce의 파라미터는 두개가 더 있긴하다.
- currentIndex: 배열의 현재 인덱스. (선택 사항)
- array: reduce가 호출된 배열. (선택 사항)
const categoryIndex = acc.findIndex(item => item.name === cur.ledger_category);
메서드 내부에서 또 하나의 메서드를 동작시킨다. "findIndex" 이것은 배열을 하나씩 돌면서 내가 찾고자하는 요소가 몇번째에 있는지를 찾아 인덱스를 반환해준다.
즉, 여기서는 축적된 배열(acc)의 요소들 중에 "name"이 현재요소(cur)의 "ledger_category"이 있는지를 확인해보고, 있으면 해당 인덱스
를, 없으면 -1
을 반환한다.
(예시 이미지)
결과
처음에는 당연히 acc가 빈배열 상태이므로 -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이 두번쨰 요소로 바뀐다.
const categoryIndex = acc.findIndex(item => item.name === cur.ledger_category);
다시만난 이 친구. 카테고리는 또 쇼핑이다. 이제는 name:'쇼핑'
을 가지고 있다.
그렇다면 조건문에 따라 다음 코드가 실행된다.
} else {
acc[categoryIndex].data.push(cur.monthly_sum_count);
acc의 "쇼핑"이 있는 요소의 데이터에 "ledger_category" 값만 push해 넣는다.
과정을 끝까지 반복하면, 최종적으로 우리가 원하는 결과를 반환하게 된다.