두 개의 리터럴 배열이 다음과 같이 있다고 가정해 보겠습니다.
const arr1 = ['uno', 'dos', 'tres', 'cuatro'];
const arr2 = ['dos', 'cuatro'];
이때 우리는 이 두 배열을 인수로 받아, 첫 번째 배열에 있으면서 두 번째 배열에도 포함된 요소를 모두 삭제하는 자바스크립트 함수를 작성해야 합니다.
즉, 위 배열을 처리한 결과는 다음과 같아야 합니다.
const output = ['uno', 'tres'];
filter() 메서드로 차집합 구현하기
배열의 내장 메서드인 filter()를 활용하면 아주 간단하게 해결할 수 있습니다. filter()는 콜백 함수가 true를 반환하는 요소만 골라 새로운 배열을 생성하므로, "두 번째 배열에 존재하지 않는 요소"만 걸러내는 조건을 넣어주면 됩니다.
예제 코드
const arr1 = ['uno', 'dos', 'tres', 'cuatro'];
const arr2 = ['dos', 'cuatro'];
const findSubtraction = (arr1 = [], arr2 = []) => {
let filtered = [];
filtered = arr1.filter(el => {
if(arr2.indexOf(el) === -1){
return true;
};
});
return filtered;
};
console.log(findSubtraction(arr1, arr2));
실행 결과
콘솔에 출력되는 결과는 다음과 같습니다.
[ 'uno', 'tres' ]
코드 동작 원리
arr1.filter(...): 첫 번째 배열의 각 요소를 순회하며 콜백 함수를 실행합니다.arr2.indexOf(el) === -1: 현재 요소가 두 번째 배열에 없다면indexOf()는 -1을 반환합니다. 즉, 두 번째 배열에 없는 요소만 true를 반환해 결과 배열에 포함됩니다.- 그 결과, 첫 번째 배열에서 두 번째 배열과 겹치는 요소('dos', 'cuatro')가 모두 제거됩니다.
더 깔끔하게: includes() 활용하기
indexOf() 대신 ES2016(ES7)에서 도입된 includes()를 사용하면 코드가 한층 직관적이고 간결해집니다.
const findSubtraction = (arr1 = [], arr2 = []) =>
arr1.filter(el => !arr2.includes(el));
console.log(findSubtraction(arr1, arr2));
// [ 'uno', 'tres' ]
성능 최적화: Set 사용하기
배열의 크기가 클 경우 includes()나 indexOf()는 요소를 찾을 때마다 선형 시간(O(n))이 소요되므로, 전체 복잡도가 O(n×m)까지 늘어날 수 있습니다. 이때 Set을 활용하면 탐색이 평균 O(1)로 처리되어 성능을 크게 개선할 수 있습니다.
const findSubtraction = (arr1 = [], arr2 = []) => {
const excludeSet = new Set(arr2);
return arr1.filter(el => !excludeSet.has(el));
};마무리
두 배열의 차집합은 filter() 하나만으로 손쉽게 구현할 수 있습니다. 데이터 양이 적다면 가독성이 좋은 includes() 방식을, 대용량 데이터라면 Set 기반 접근을 선택하는 것이 효율적입니다.