문제 상황
실무에서 자주 마주치는 상황 중 하나는, 한 배열의 객체들이 다른 배열에 이미 존재하는지 확인하고 그에 따라 데이터를 걸러내는 작업입니다. 다음과 같이 객체를 담고 있는 두 개의 배열이 있다고 가정해 보겠습니다.
const arr1 = [{id:'1',name:'A'},{id:'2',name:'B'},{id:'3',name:'C'},{id:'4',name:'D'}];
const arr2 = [{id:'1',name:'A',state:'healthy'},{id:'3',name:'C',state:'healthy'}];우리가 작성해야 할 함수는 이 두 배열을 인수로 받아, 첫 번째 배열(arr1) 중에서 두 번째 배열(arr2)에 동일한 속성 값을 가진 객체가 존재하지 않는 요소들만 모아 새로운 배열로 반환해야 합니다.
따라서 위 예제의 기대 출력 결과는 다음과 같습니다.
const output = [{id:'2',name:'B'},{id:'4',name:'D'}];예제 코드
배열 내장 메서드인 filter()와 find()를 조합하면 이 문제를 간결하게 해결할 수 있습니다.
const arr1 = [{id:'1',name:'A'},{id:'2',name:'B'},{id:'3',name:'C'},{id:'4',name:'D'}];
const arr2 = [{id:'1',name:'A',state:'healthy'},{id:'3',name:'C',state:'healthy'}];
const filterByReference = (arr1, arr2) => {
let res = [];
res = arr1.filter(el => {
return !arr2.find(element => {
return element.id === el.id;
});
});
return res;
}
console.log(filterByReference(arr1, arr2));코드 동작 원리
arr1.filter(...)— arr1의 각 요소를 순회하며 콜백 함수가 true를 반환하는 요소만 새 배열에 남깁니다.!arr2.find(...)— arr2 안에서 현재 요소와 동일한 id를 가진 객체를 탐색합니다. 일치하는 항목이 없으면find()는 undefined를 반환하고, 논리 부정 연산자(!)에 의해 해당 요소가 결과 배열에 포함됩니다.
실행 결과
콘솔에 출력되는 결과는 다음과 같습니다.
[ { id: '2', name: 'B' }, { id: '4', name: 'D' } ]성능 개선 팁
배열의 크기가 커질 경우 주의할 점이 있습니다. find()는 매 호출마다 선형 탐색을 수행하므로, 전체 시간 복잡도가 O(n×m)까지 늘어날 수 있습니다. 대량의 데이터를 다룬다면 Set을 활용해 조회 비용을 O(1)로 줄이는 것이 좋습니다.
const filterByReference = (arr1, arr2) => {
const ids = new Set(arr2.map(el => el.id));
return arr1.filter(el => !ids.has(el.id));
};이처럼 먼저 arr2의 id 값들을 Set으로 만들어 두면, 각 요소를 상수 시간에 비교할 수 있어 전체 복잡도를 O(n+m)으로 최적화할 수 있습니다.