문제 상황
다음과 같이 티켓 정보가 담긴 JSON 배열이 있다고 가정해 보겠습니다.
const arr = [
{
"quantity": "1",
"description": "VIP Ticket to Event"
},
{
"quantity": "1",
"description": "VIP Ticket to Event"
},
{
"quantity": "1",
"description": "VIP Ticket to Event"
},
{
"quantity": "1",
"description": "Regular Ticket to Event"
},
{
"quantity": "1",
"description": "Regular Ticket to Event"
}
];
이 배열을 입력받아 description 속성 값이 같은 객체들을 하나의 그룹으로 묶고, 각 그룹의 quantity 값을 모두 합산하는 JavaScript 함수를 작성해야 합니다. 즉, 두 객체가 동일하다고 판단하는 기준은 description 속성의 값이 서로 같은지 여부입니다.
해결 방법
임시 객체(맵)를 키-값 저장소로 활용하면 배열을 한 번만 순회하면서 그룹화와 합산을 동시에 처리할 수 있습니다. 이미 등록된 description이라면 해당 항목의 quantity에 값을 누적하고, 처음 등장한 description이라면 새 항목을 만들어 결과 배열에 추가합니다.
// 앞서 정의한 arr 배열을 사용합니다.
const groupAndAdd = arr => {
const map = {};
const res = [];
arr.forEach(el => {
if (!map[el.description]) {
map[el.description] = {
description: el.description,
quantity: 0
};
res.push(map[el.description]);
}
map[el.description].quantity += +el.quantity;
});
return res;
};
console.log(groupAndAdd(arr));
코드 설명
- map: description을 키로 사용하는 임시 객체로, 중복 여부를 빠르게 확인합니다.
- res: 최종적으로 반환될 그룹화된 배열입니다.
- +el.quantity: 단항 덧셈 연산자로 문자열인 수량을 숫자로 변환하여 누적합니다.
참고로 일부 예제에서는 화살표 함수 내부에서 this를 사용하지만, 화살표 함수는 forEach의 thisArg를 무시하므로 실행 환경에 따라 오류가 발생할 수 있습니다. 위 코드처럼 지역 변수 맵을 사용하는 것이 더 안전하고 명확한 방식입니다.
reduce()를 활용한 대안
Array.prototype.reduce()를 사용하면 같은 로직을 더 간결하게 표현할 수도 있습니다.
const grouped = Object.values(
arr.reduce((acc, { quantity, description }) => {
acc[description] ??= { description, quantity: 0 };
acc[description].quantity += Number(quantity);
return acc;
}, {})
);
console.log(grouped);
실행 결과
콘솔에는 다음과 같이 출력됩니다.
[
{ description: 'VIP Ticket to Event', quantity: 3 },
{ description: 'Regular Ticket to Event', quantity: 2 }
]
VIP 티켓 3장과 일반 티켓 2장이 각각 하나의 객체로 묶여 수량이 정확히 합산된 것을 확인할 수 있습니다. 이 방식은 배열을 한 번만 순회하므로 시간 복잡도가 O(n)이며, 데이터 양이 많아져도 효율적으로 동작한다는 장점이 있습니다.