문제 소개
실무에서는 서로 다른 시점에 수집된 데이터가 하나의 배열에 섞여 있는 경우가 많습니다. 예를 들어 다음과 같이 date와 price 또는 price2를 가진 객체들이 배열에 담겨 있다고 가정해 보겠습니다.
const arr = [
{ "date": "2010-01-01", "price": 30 },
{ "date": "2010-02-01", "price": 40 },
{ "date": "2010-03-01", "price": 50 },
{ "date": "2010-01-01", "price2": 45 },
{ "date": "2010-05-01", "price2": 40 },
{ "date": "2010-10-01", "price2": 50 }
];
여기서 우리가 작성해야 할 것은 이러한 배열을 입력받아 객체의 'date'(날짜) 속성을 기준으로 객체들을 병합하는 JavaScript 함수입니다. 같은 날짜를 가진 객체는 하나로 합쳐지며, 해당 날짜에 값이 존재하지 않는 속성은 null로 채워집니다.
예제 코드
다음은 배열을 날짜순으로 정렬한 뒤, 같은 날짜의 객체를 하나로 묶어주는 전체 구현 예제입니다.
const arr = [
{ "date": "2010-01-01", "price": 30 },
{ "date": "2010-02-01", "price": 40 },
{ "date": "2010-03-01", "price": 50 },
{ "date": "2010-01-01", "price2": 45 },
{ "date": "2010-05-01", "price2": 40 },
{ "date": "2010-10-01", "price2": 50 }
];
const mergeArray = (arr = []) => {
// 원본 배열 보호를 위해 복사본 생성
const data = arr.slice();
// 날짜 오름차순 정렬
data.sort((a, b) => new Date(a.date) - new Date(b.date));
const res = []; // 최종 결과 배열
const map = {}; // 날짜별 병합 객체를 추적하기 위한 임시 맵
data.forEach(el => {
if (!map[el.date]) {
// 해당 날짜가 처음 등장하면 기본 객체를 생성하고 결과에 추가
map[el.date] = {
date: el.date,
price: null,
price2: null
};
res.push(map[el.date]);
}
// 같은 날짜라면 기존 객체에 속성을 덮어써 병합
Object.assign(map[el.date], el);
});
return res;
};
console.log(JSON.stringify(mergeArray(arr), undefined, 4));
코드 동작 방식
- 복사본 생성:
slice()로 원본 배열을 복사하여 원본 데이터가 변경되지 않도록 합니다. - 날짜 정렬:
new Date()로 문자열 날짜를 Date 객체로 변환한 뒤 그 차이를 비교해 오름차순으로 정렬합니다. - 중복 확인:
map객체로 해당 날짜가 이미 처리되었는지 확인하고, 처음 등장한 날짜라면price와price2가null인 기본 객체를 만들어 결과 배열에 추가합니다. - 병합:
Object.assign()으로 현재 객체의 속성을 기본 객체에 덮어써, 같은 날짜의 데이터가 하나로 합쳐지도록 합니다.
참고: 일부 예제에서는 임시 저장소로this를 사용하기도 하지만, 화살표 함수 안의this는 실행 환경에 따라 달라질 수 있으므로 위처럼 지역 변수 객체를 사용하는 것이 더 안전하고 명확합니다.
실행 결과
위 코드를 실행하면 콘솔에 다음과 같은 결과가 출력됩니다.
[
{
"date": "2010-01-01",
"price": 30,
"price2": 45
},
{
"date": "2010-02-01",
"price": 40,
"price2": null
},
{
"date": "2010-03-01",
"price": 50,
"price2": null
},
{
"date": "2010-05-01",
"price": null,
"price2": 40
},
{
"date": "2010-10-01",
"price": null,
"price2": 50
}
]
결과 분석
'2010-01-01'에는 price(30)와 price2(45)가 모두 존재하므로 두 객체가 하나로 병합되었습니다. 반면 '2010-02-01'에는 price만 있기 때문에 price2는 null로 남습니다. 이처럼 날짜를 기준으로 데이터를 통합하면 시계열 차트를 그리거나 날짜별 리포트를 작성할 때 매우 유용하게 활용할 수 있습니다.