실무 개발에서는 학생 성적 데이터처럼 중복된 키를 가진 객체 배열을 하나의 요약된 배열로 묶어야 하는 경우가 자주 발생합니다. 이 글에서는 'subject' 속성을 기준으로 중복 항목을 제거하고, 각 고유 항목의 'marks'와 'noOfStudents' 값을 합산하는 방법을 예제와 함께 살펴보겠습니다.
문제 상황
다음과 같이 학생들의 과목별 점수와 인원수가 담긴 객체 배열이 있다고 가정해 보겠습니다.
const arr = [
{ subject: 'Maths', marks: '40', noOfStudents: '5' },
{ subject: 'Science', marks: '50', noOfStudents: '16' },
{ subject: 'History', marks: '35', noOfStudents: '23' },
{ subject: 'Science', marks: '65', noOfStudents: '2' },
{ subject: 'Maths', marks: '30', noOfStudents: '12' },
{ subject: 'History', marks: '55', noOfStudents: '20' },
];
우리가 작성해야 할 함수는 다음 두 가지 작업을 수행해야 합니다.
- 객체의
subject속성을 기준으로 중복된 항목을 제거할 것 - 동일한 과목을 가진 객체들의
marks와noOfStudents값을 하나의 객체에 모두 더할 것
따라서 위 배열에 대한 최종 출력은 다음과 같아야 합니다.
const output = [
{ subject: 'Maths', marks: '70', noOfStudents: '17' },
{ subject: 'Science', marks: '115', noOfStudents: '18' },
{ subject: 'History', marks: '95', noOfStudents: '43' },
];
해결 코드
reduce() 메서드와 임시 맵(map) 객체를 함께 활용하면 배열을 단 한 번만 순회하면서 문제를 해결할 수 있습니다.
const arr = [
{ subject: 'Maths', marks: '40', noOfStudents: '5' },
{ subject: 'Science', marks: '50', noOfStudents: '16' },
{ subject: 'History', marks: '35', noOfStudents: '23' },
{ subject: 'Science', marks: '65', noOfStudents: '2' },
{ subject: 'Maths', marks: '30', noOfStudents: '12' },
{ subject: 'History', marks: '55', noOfStudents: '20' },
];
const groupBySubject = (arr = []) => {
// 과목명이 결과 배열의 어느 인덱스에 있는지 추적하는 맵
const map = {};
return arr.reduce((acc, val) => {
const { subject, marks, noOfStudents } = val;
if (map.hasOwnProperty(subject)) {
// 이미 존재하는 과목이라면 기존 값에 누적 (+ 연산자로 문자열을 숫자로 변환)
acc[map[subject]].marks += +marks;
acc[map[subject]].noOfStudents += +noOfStudents;
} else {
// 처음 등장한 과목이라면 새 항목으로 추가
map[subject] = acc.length;
acc.push({
subject,
marks: +marks,
noOfStudents: +noOfStudents
});
}
return acc;
}, []);
};
console.log(groupBySubject(arr));
실행 결과
콘솔에는 다음과 같이 과목별로 합산된 결과가 출력됩니다.
[
{ subject: 'Maths', marks: 70, noOfStudents: 17 },
{ subject: 'Science', marks: 115, noOfStudents: 18 },
{ subject: 'History', marks: 95, noOfStudents: 43 }
]
코드 동작 원리
- 맵(map) 객체의 역할: 각 과목명이 결과 배열의 몇 번째 인덱스에 위치하는지 저장합니다. 덕분에 해당 과목이 이미 처리되었는지 O(1) 시간 복잡도로 빠르게 확인할 수 있습니다.
- 단항 + 연산자: 원본 데이터의
marks와noOfStudents는 문자열입니다.+value형태로 변환해야 정상적인 산술 연산이 가능합니다. - 누적 로직 주의점: 중복 과목을 만났을 때 값을 단순히 덮어쓰면(
=) 마지막 값만 남게 됩니다. 반드시 복합 대입 연산자(+=)를 사용해 기존 값에 더해 주어야 의도한 합계가 나옵니다. - reduce() 메서드: 배열을 순회하면서 누산기(acc)에 결과를 지속적으로 축적하고, 최종적으로 그룹화된 배열 하나를 반환합니다.
이 패턴은 성적 집계 외에도 장바구니 합산, 로그 데이터 통계, 카테고리별 매출 집계 등 다양한 실무 시나리오에 그대로 응용할 수 있으니 꼭 익혀 두시기 바랍니다.