Computer >> 컴퓨터 >  >> 프로그래밍 >> JavaScript

JavaScript reduce() 메서드로 공통 필드 기준 배열 병합하는 방법

문제 상황

다음과 같이 여러 개의 객체를 담고 있는 배열이 있다고 가정해 보겠습니다.

const arr = [{
    id: 121,
    hobby: 'cycling'
}, {
    id: 125,
    hobby: 'jogging'
}, {
    id: 129,
    hobby: 'reading'
}, {
    id: 121,
    hobby: 'writing'
}, {
    id: 121,
    hobby: 'playing football'
}, {
    id: 125,
    hobby: 'cooking'
}, {
    id: 129,
    hobby: 'horse riding'
}];

여기서 우리가 만들어야 할 함수는 이런 배열을 입력받아 공통된 id 속성을 기준으로 항목들을 병합하는 역할을 합니다. 병합 과정에서 hobby 속성은 배열 형태로 변환하여, 같은 id를 가진 모든 취미 값을 해당 배열 안에 담아야 합니다.

해결 방법: Array.prototype.reduce()

이 문제는 Array.prototype.reduce() 메서드를 사용하면 깔끔하게 해결할 수 있습니다. reduce()는 배열을 순회하면서 누적값(accumulator)을 관리할 수 있기 때문에, 반복 도중 동일한 id를 가진 항목들을 하나로 합치기에 적합합니다.

동작 방식은 다음과 같습니다.

  • 누적 배열(acc)에서 현재 항목과 같은 id를 가진 요소의 인덱스를 findIndex()로 찾습니다.
  • 같은 id가 이미 존재한다면(ind !== -1), 해당 요소의 hobby 배열에 새 취미를 추가합니다.
  • 존재하지 않는다면, 새로운 객체를 만들어 누적 배열에 push합니다.

예제 코드

const arr = [{
    id: 121,
    hobby: 'cycling'
}, {
    id: 125,
    hobby: 'jogging'
}, {
    id: 129,
    hobby: 'reading'
}, {
    id: 121,
    hobby: 'writing'
}, {
    id: 121,
    hobby: 'playing football'
}, {
    id: 125,
    hobby: 'cooking'
}, {
    id: 129,
    hobby: 'horse riding'
}];

const mergeArray = (arr) => {
    return arr.reduce((acc, val) => {
        const ind = acc.findIndex(item => item.id === val.id);
        if(ind !== -1){
            acc[ind].hobby.push(val.hobby);
        }else{
            acc.push({
                id: val.id,
                hobby: [val.hobby]
            });
        }
        return acc;
    }, []);
};

console.log(mergeArray(arr));

실행 결과

위 코드를 실행하면 콘솔에 다음과 같은 결과가 출력됩니다.

[
    { id: 121, hobby: [ 'cycling', 'writing', 'playing football' ] },
    { id: 125, hobby: [ 'jogging', 'cooking' ] },
    { id: 129, hobby: [ 'reading', 'horse riding' ] }
]

정리

reduce()와 findIndex()를 조합하면 중복된 키 값을 기준으로 객체 배열을 손쉽게 그룹화할 수 있습니다. 다만 findIndex()는 각 반복마다 배열을 처음부터 탐색하므로, 데이터 양이 매우 클 경우에는 Map이나 객체를 해시 맵처럼 활용해 O(n) 성능으로 최적화하는 것도 고려해 볼 수 있습니다.