문제 소개
다음과 같이 고객(customer)과 프로젝트(project) 정보를 담은 객체 배열이 있다고 가정해 보겠습니다.
const arr = [
{
"customer": "Customer 1",
"project": "1"
},
{
"customer": "Customer 2",
"project": "2"
},
{
"customer": "Customer 2",
"project": "3"
}
]우리가 작성해야 할 것은 이러한 배열을 입력받아 새로운 배열을 반환하는 JavaScript 함수입니다.
새 배열에서는 동일한 customer 값을 가진 항목들이 하나로 병합되어야 하며, 최종적으로 다음과 같은 형태의 결과를 얻는 것이 목표입니다.
const output = [
{
"Customer 1": {
"projects": "1"
}
},
{
"Customer 2": {
"projects": [
"2",
"3"
]
}
}
]즉, 각 고객별로 프로젝트 목록을 묶어 주는 그룹화(grouping) 작업이라고 할 수 있습니다.
forEach를 활용한 해결 방법
먼저 forEach 메서드를 이용해 구현해 보겠습니다.
const arr = [
{
"customer": "Customer 1",
"project": "1"
},
{
"customer": "Customer 2",
"project": "2"
},
{
"customer": "Customer 2",
"project": "3"
}
];
const groupCustomer = data => {
const res = [];
data.forEach(el => {
// 결과 배열에 이미 존재하는 고객인지 확인
let customer = res.filter(custom => {
return el.customer === custom.customer;
})[0];
if (customer) {
// 기존 고객이면 프로젝트만 추가
customer.projects.push(el.project);
} else {
// 새로운 고객이면 새 항목 생성
res.push({ customer: el.customer, projects: [el.project] });
}
});
return res;
};
console.log(groupCustomer(arr));출력 결과
위 코드를 실행하면 콘솔에 다음과 같이 출력됩니다.
[
{ customer: 'Customer 1', projects: [ '1' ] },
{ customer: 'Customer 2', projects: [ '2', '3' ] }
]실행 결과를 보면 Customer 2처럼 중복된 고객은 하나의 객체로 합쳐지고, 프로젝트 값들이 배열 형태로 누적되는 것을 확인할 수 있습니다.
코드 동작 원리
- 결과 배열 초기화: 빈 배열 res를 만들어 그룹화된 데이터를 담을 준비를 합니다.
- 배열 순회: forEach로 원본 배열의 각 객체를 하나씩 확인합니다.
- 중복 검사: filter를 사용해 res 안에 같은 customer 값을 가진 항목이 있는지 찾습니다.
- 분기 처리: 이미 존재하는 고객이면 해당 객체의 projects 배열에 프로젝트를 push하고, 없다면 새 객체를 만들어 추가합니다.
reduce를 활용한 대안
같은 로직을 reduce 메서드로 더 간결하게 표현할 수도 있습니다.
const groupByReduce = data =>
data.reduce((acc, el) => {
const existing = acc.find(item => item.customer === el.customer);
if (existing) {
existing.projects.push(el.project);
} else {
acc.push({ customer: el.customer, projects: [el.project] });
}
return acc;
}, []);
console.log(groupByReduce(arr));reduce는 누적값(acc)을 활용해 반복과 결과 생성을 한 번에 처리하기 때문에, 이런 그룹화 작업에서 특히 유용하게 사용됩니다.
마무리
이처럼 JavaScript에서는 forEach나 reduce 같은 배열 메서드를 활용하면 객체 배열을 손쉽게 반복하면서 조건에 맞게 재구성한 새 배열을 만들 수 있습니다. 참고로 위 방식은 매번 filter를 호출하기 때문에 데이터가 많아지면 O(n²)에 가깝게 느려질 수 있습니다. 이 경우 Map이나 일반 객체를 해시맵처럼 활용하면 O(n) 수준으로 성능을 개선할 수 있으니, 대량의 데이터를 다룰 때는 이 점을 함께 고려해 보시기 바랍니다.