실무에서는 객체 안에 또 다른 객체나 배열이 중첩된 형태의 데이터를 자주 다루게 됩니다. 이번 글에서는 이렇게 중첩된 배열에서 특정 조건을 만족하는 객체만 골라내는 방법을 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));코드 동작 원리
핵심 로직을 단계별로 살펴보면 다음과 같습니다.
- reduce()로 조회 맵 생성: 검색 키 배열인
keys를reduce()로 순회하면서{ Pegasus: true }형태의 객체로 변환합니다. 이렇게 하면 문자열 비교 대신 객체 속성 조회(O(1))로 빠르게 존재 여부를 확인할 수 있습니다. - filter()로 최상위 배열 순회:
arr.filter()의 두 번째 인자로 this 값을 지정하면, 콜백 함수 내부에서thisObj를 통해 앞서 만든 조회 맵에 접근할 수 있습니다. - 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 객체는 필터링되어 제외된 것을 확인할 수 있습니다.