데이터를 다루다 보면 배열 안에 여러 개의 배열이 중첩된 2차원(2D) 테이블 형태의 데이터를 자주 접하게 됩니다. 이런 구조는 사람이 읽기에는 직관적이지만, 프로그래밍으로 활용할 때는 각 행을 의미 있는 속성을 가진 객체로 변환하는 것이 훨씬 편리합니다.
이번 글에서는 2차원 배열 데이터를 첫 번째 열의 값을 기준으로 그룹화하여, 하나의 객체로 묶인 새로운 배열로 변환하는 방법을 알아보겠습니다.
문제 상황
예를 들어, 다음과 같이 이름, 날짜, 점수가 담긴 배열의 배열이 있다고 가정해 보겠습니다.
const arr = [ ["Ashley","2017-01-10",80], ["Ashley","2017-02-10",75], ["Ashley","2017-03-10",85], ["Clara","2017-01-10",90], ["Clara","2017-02-10",82] ];
여기서 우리가 만들 함수는 위와 같은 배열을 유일한 입력값으로 받습니다. 그런 다음 입력 배열을 기반으로 새로운 객체 배열을 생성해야 합니다.
결과 배열에는 입력 배열의 고유한 하위 배열마다 하나의 객체가 존재해야 합니다. 여기서 '고유하다'는 것은 하위 배열의 첫 번째 요소(이름)가 서로 다르다는 의미입니다. 즉, 같은 이름을 가진 행들은 하나의 객체로 합쳐지고, 날짜는 키(key), 점수는 값(value)이 됩니다.
원하는 출력 형식
각 객체는 다음과 같은 구조를 가져야 합니다.
const output = [
{"name":"Ashley", "2017-01-10":80, "2017-02-10":75, "2017-03-10":85},
{"name":"Clara", "2017-01-10":90, "2017-02-10":82}
];해결 방법: reduce()를 활용한 그룹화
이 문제는 JavaScript의 reduce() 메서드와 해시 역할을 하는 임시 객체를 함께 사용하면 깔끔하게 해결할 수 있습니다. 아래 예제 코드를 살펴보겠습니다.
const arr = [
["Ashley","2017-01-10",80],
["Ashley","2017-02-10",75],
["Ashley","2017-03-10",85],
["Clara","2017-01-10",90],
["Clara","2017-02-10",82]
];
const groupArray = (arr = []) => {
let grouped = [];
grouped = arr.reduce(function (hash) {
return function (r, a) {
// 해당 이름의 객체가 아직 없다면 새로 생성
if (!hash[a[0]]) {
hash[a[0]] = { name: a[0] };
r.push(hash[a[0]]);
}
// 날짜를 키로, 점수를 값으로 저장
hash[a[0]][a[1]] = a[2];
return r;
};
}(Object.create(null)), []);
return grouped;
}
console.log(groupArray(arr));코드 동작 원리
이 코드의 핵심 로직은 다음과 같이 정리할 수 있습니다.
1. 해시 객체 생성: Object.create(null)로 프로토타입이 없는 순수한 객체를 만들어, 각 이름별 결과 객체를 빠르게 조회하고 저장하는 캐시(hash)로 사용합니다. 프로토타입 체인이 없기 때문에 toString 같은 내장 키와 충돌할 걱정도 없습니다.
2. 그룹 판별: 각 하위 배열을 순회하면서 hash[a[0]], 즉 해당 이름의 객체가 존재하지 않으면 { name: a[0] } 형태로 새 객체를 만들고, 결과 배열 r에 push합니다.
3. 동적 키 할당: 두 번째 요소(날짜)를 키로, 세 번째 요소(점수)를 값으로 하여 기존 객체에 계속 추가합니다. 같은 이름의 행이 여러 번 등장해도 모두 하나의 객체에 누적됩니다.
실행 결과
위 코드를 실행하면 콘솔에 다음과 같은 결과가 출력됩니다.
[
{ name: 'Ashley',
'2017-01-10': 80,
'2017-02-10': 75,
'2017-03-10': 85
},
{ name: 'Clara',
'2017-01-10': 90,
'2017-02-10': 82 }
]Ashley의 세 개 행과 Clara의 두 개 행이 각각 하나의 객체로 잘 병합된 것을 확인할 수 있습니다.
마무리
이처럼 reduce()와 해시 객체를 조합하면 2차원 테이블 데이터를 특정 열을 기준으로 그룹화하여 객체 배열로 손쉽게 변환할 수 있습니다. 이 패턴은 CSV 파싱 결과 가공, API 응답 데이터 재구성, 피벗 테이블 형태의 데이터 생성 등 실무에서 매우 다양하게 활용되므로 꼭 익혀두시길 추천합니다.