두 개의 숫자 배열이 있다고 가정해 보겠습니다.
const arr1 = [12, 54, 2, 4, 6, 34, 3];
const arr2 = [54, 2, 5, 12, 4, 1, 3, 34];
위 배열을 살펴보면 6은 첫 번째 배열(arr1)에만 존재하고, 5와 1은 두 번째 배열(arr2)에만 존재합니다. 나머지 요소들은 두 배열 모두에 공통으로 포함되어 있습니다.
이번 글에서는 이처럼 두 개의 배열을 인수로 받아, 양쪽 배열에 공통으로 존재하지 않는 요소들만 골라 새로운 배열로 반환하는 JavaScript 함수를 작성해 보겠습니다.
구현 아이디어
핵심 로직은 다음과 같습니다.
- 첫 번째 배열을 순회하면서 각 요소가 두 번째 배열에 존재하는지
indexOf()로 확인합니다. 결과가-1이면 해당 요소는 두 번째 배열에 없다는 뜻이므로 결과 배열에 추가합니다. - 같은 방식으로 두 번째 배열을 순회하며 첫 번째 배열에 없는 요소를 결과 배열에 추가합니다.
- 모든 순회가 끝나면 결과 배열을 반환합니다.
예제 코드
다음은 전체 구현 코드입니다.
const arr1 = [12, 54, 2, 4, 6, 34, 3];
const arr2 = [54, 2, 5, 12, 4, 1, 3, 34];
const unCommonArray = (first, second) => {
const res = [];
// 첫 번째 배열에서 두 번째 배열에 없는 요소 찾기
for(let i = 0; i < first.length; i++){
if(second.indexOf(first[i]) === -1){
res.push(first[i]);
}
};
// 두 번째 배열에서 첫 번째 배열에 없는 요소 찾기
for(let j = 0; j < second.length; j++){
if(first.indexOf(second[j]) === -1){
res.push(second[j]);
};
};
return res;
};
console.log(unCommonArray(arr1, arr2));
실행 결과
콘솔에 출력되는 결과는 다음과 같습니다.
[ 6, 5, 1 ]
arr1에만 있는 6, arr2에만 있는 5와 1이 정확히 추출된 것을 확인할 수 있습니다.
더 간결한 대안: filter() 활용
ES6의 filter()와 스프레드 연산자를 사용하면 같은 로직을 훨씬 간결하게 표현할 수 있습니다.
const unCommonArray = (first, second) => [
...first.filter(el => !second.includes(el)),
...second.filter(el => !first.includes(el))
];
console.log(unCommonArray(arr1, arr2)); // [ 6, 5, 1 ]
filter()는 조건을 만족하는 요소만 남긴 새 배열을 반환하므로, 반복문과 push()를 직접 작성할 필요 없이 선언형으로 깔끔하게 처리됩니다. 가독성 면에서는 이 방식이 더 권장됩니다.
정리
두 배열의 대칭 차집합(symmetric difference), 즉 한쪽에만 존재하는 요소를 구하는 문제는 반복문 + indexOf() 조합 또는 filter() + includes() 조합으로 손쉽게 해결할 수 있습니다. 단, 두 방식 모두 내부적으로 배열을 선형 탐색하므로 시간 복잡도는 O(n×m)입니다. 배열의 크기가 매우 크다면 Set 객체를 활용해 조회 성능을 O(1)로 개선하는 방법도 고려해볼 만합니다.