JavaScript 개발을 하다 보면 두 개의 배열을 비교해서 특정 조건에 맞는 요소만 추출해야 하는 경우가 자주 발생합니다. 이번 글에서는 숫자 리터럴 배열과 객체 배열이 주어졌을 때, 객체의 특정 키 값이 리터럴 배열에 존재하는 요소만 골라내는 방법을 예제와 함께 살펴보겠습니다.
문제 상황
다음과 같이 두 개의 배열이 있다고 가정해 보겠습니다. 하나는 단순한 숫자 값의 배열이고, 다른 하나는 city 키를 가진 객체들의 배열입니다.
const source = [1, 2, 3, 4, 5];
const cities = [{ city: 4 }, { city: 6 }, { city: 8 }];
우리가 만들어야 할 함수는 이 두 배열을 인자로 받아, 객체 배열 중 city 키의 값이 숫자 배열(source)에 포함되어 있는 요소들만 모아 새로운 배열로 반환하는 역할을 합니다.
해결 코드
Array.prototype.filter() 메서드와 Array.prototype.includes() 메서드를 조합하면 아주 간결하게 문제를 해결할 수 있습니다.
const source = [1, 2, 3, 4, 5];
const cities = [{ city: 4 }, { city: 6 }, { city: 8 }];
const filterByLiterals = (objArr, literalArr) => {
const common = objArr.filter(el => {
return literalArr.includes(el['city']);
});
return common;
};
console.log(filterByLiterals(cities, source));
실행 결과
콘솔에 출력되는 결과는 다음과 같습니다.
[ { city: 4 } ]
코드 동작 원리
filter() — 배열의 각 요소를 순회하면서 콜백 함수가 true를 반환하는 요소만 모아 새로운 배열을 생성합니다. 원본 배열은 변경되지 않으므로 안전하게 사용할 수 있습니다.
includes() — 배열에 특정 값이 포함되어 있는지 여부를 true 또는 false로 반환합니다.
즉, cities 배열의 각 객체에서 el['city'] 값(4, 6, 8)을 하나씩 꺼내 source 배열([1, 2, 3, 4, 5])에 포함되어 있는지 검사하고, 포함된 경우(4)만 새 배열에 담게 됩니다.
Set을 활용한 성능 최적화
처리해야 할 데이터의 양이 많다면 includes() 대신 Set을 사용하는 것이 좋습니다. includes()는 배열 전체를 순차적으로 탐색하는 선형 탐색(O(n))인 반면, Set의 has() 메서드는 평균적으로 O(1)의 시간 복잡도를 가지기 때문입니다.
const filterByLiterals = (objArr, literalArr) => {
const lookup = new Set(literalArr);
return objArr.filter(el => lookup.has(el['city']));
};
console.log(filterByLiterals(cities, source)); // [ { city: 4 } ]
두 방식 모두 결과는 동일하지만, 대량의 데이터를 다룰 때는 Set 기반 접근이 훨씬 효율적입니다. 상황에 맞게 적절한 방법을 선택해 활용해 보시기 바랍니다.