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

JavaScript로 중첩된 객체 배열 필터링하기 — filter와 some 활용법

실무에서는 객체 안에 또 다른 객체나 배열이 중첩된 형태의 데이터를 자주 다루게 됩니다. 이번 글에서는 이렇게 중첩된 배열에서 특정 조건을 만족하는 객체만 골라내는 방법을 JavaScript 코드 예제와 함께 살펴보겠습니다.

문제 상황

다음과 같이 각 항목이 legs라는 하위 배열을 가지고 있는 객체 배열이 있다고 가정해 보겠습니다.

const arr = [{
    id: 1,
    legs: [{
        carrierName: 'Pegasus'
    }]
},
{
    id: 2,
    legs: [{
        carrierName: 'SunExpress'
    },
    {
        carrierName: 'SunExpress'
    }]
},
{
    id: 3,
    legs: [{
        carrierName: 'Pegasus'
    },
    {
        carrierName: 'SunExpress'
    }]
}];

여기서 우리가 작성해야 할 함수는 다음과 같은 역할을 합니다.

  • 첫 번째 인자로 위와 같은 객체 배열을 받습니다.
  • 두 번째 인자로 검색할 문자열(예: 항공사 이름)을 받습니다.
  • 하위 legs 배열 안의 carrierName 속성 값이 두 번째 인자와 일치하는 객체만 남겨서 새로운 배열을 반환합니다.

예를 들어 두 번째 인자로 'Pegasus'를 전달하면, carrierName에 'Pegasus'가 하나라도 포함된 객체들만 결과에 남아야 합니다. 즉, id가 2인 객체는 제외되고 id가 1과 3인 객체만 반환됩니다.

const output = [{
    id: 1,
    legs: [{
        carrierName: 'Pegasus'
    }]
},
{
    id: 3,
    legs: [{
        carrierName: 'Pegasus'
    },
    {
        carrierName: 'SunExpress'
    }]
}];

예제 코드

이를 구현한 전체 코드는 다음과 같습니다.

const arr = [{
    id: 1,
    legs: [{
        carrierName: 'Pegasus'
    }]
},
{
    id: 2,
    legs: [{
        carrierName: 'SunExpress'
    },
    {
        carrierName: 'SunExpress'
    }]
},
{
    id: 3,
    legs: [{
        carrierName: 'Pegasus'
    },
    {
        carrierName: 'SunExpress'
    }]
}];
const keys = ['Pegasus'];
const filterByKeys = (arr = [], keys = []) => {
    const res = arr.filter(function(item) {
        const thisObj = this;
        return item.legs.some(leg => {
            return thisObj[leg.carrierName];
        });
    }, keys.reduce((acc, val) => {
        acc[val] = true;
        return acc;
    }, Object.create(null)));
    return res;
}
console.log(JSON.stringify(filterByKeys(arr, keys), undefined, 4));

코드 동작 원리

핵심 로직을 단계별로 살펴보면 다음과 같습니다.

  1. reduce()로 조회 맵 생성: 검색 키 배열인 keysreduce()로 순회하면서 { Pegasus: true } 형태의 객체로 변환합니다. 이렇게 하면 문자열 비교 대신 객체 속성 조회(O(1))로 빠르게 존재 여부를 확인할 수 있습니다.
  2. filter()로 최상위 배열 순회: arr.filter()의 두 번째 인자로 this 값을 지정하면, 콜백 함수 내부에서 thisObj를 통해 앞서 만든 조회 맵에 접근할 수 있습니다.
  3. some()으로 중첩 배열 검사: 각 객체의 legs 배열에 대해 some()을 호출하여, 단 하나의 leg라도 조회 맵에 해당 carrierName이 존재하면 true를 반환합니다. 즉, 부분 일치만으로도 해당 객체가 결과에 포함됩니다.

이 방식의 장점은 여러 개의 검색 키를 한 번에 처리할 수 있다는 점입니다. keys 배열에 'Pegasus'와 'SunExpress'를 함께 넣으면 두 항공사 중 어느 하나라도 포함된 객체들이 모두 필터링됩니다.

실행 결과

콘솔에 출력되는 결과는 다음과 같습니다.

[
    {
        "id": 1,
        "legs": [
            {
                "carrierName": "Pegasus"
            }
        ]
    },
    {
        "id": 3,
        "legs": [
            {
                "carrierName": "Pegasus"
            },
            {
                "carrierName": "SunExpress"
            }
        ]
    }
]

예상대로 'Pegasus'를 운항하는 항공편(id: 1, id: 3)만 결과에 포함되었고, 'SunExpress'만 운항하는 id: 2 객체는 필터링되어 제외된 것을 확인할 수 있습니다.