문제 소개
다음과 같이 두 개의 숫자 배열이 있다고 가정해 보겠습니다.
const arr1 = [12, 54, 2, 4, 6, 34, 3];
const arr2 = [54, 2, 5, 12, 4, 1, 3, 34];
여기서 우리가 작성해야 할 함수는 두 배열을 인자로 받아, 양쪽 배열에 공통으로 존재하지 않는 요소들, 즉 한쪽 배열에만 있는 값들을 모아서 반환하는 것입니다. 이를 흔히 '편차' 또는 '대칭 차집합'이라고 부릅니다.
해결 로직
구현 아이디어는 매우 단순합니다.
- 첫 번째 배열을 순회하면서, 해당 요소가 두 번째 배열에 없다면 결과 배열에 추가합니다.
- 두 번째 배열을 순회하면서, 해당 요소가 첫 번째 배열에 없다면 결과 배열에 추가합니다.
특정 요소가 배열에 포함되어 있는지 확인할 때는 indexOf() 메서드를 사용할 수 있습니다. indexOf()는 요소를 찾지 못하면 -1을 반환하므로, 이 특성을 조건문에 그대로 활용하면 됩니다.
예제 코드
const arr1 = [12, 54, 2, 4, 6, 34, 3];
const arr2 = [54, 2, 5, 12, 4, 1, 3, 34];
const difference = (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(difference(arr1, arr2));
실행 결과
콘솔에 출력되는 결과는 다음과 같습니다.
[ 6, 5, 1 ]
숫자 6은 첫 번째 배열에만 존재하고, 5와 1은 두 번째 배열에만 존재하기 때문에 정확히 이 세 개의 값이 결과로 반환됩니다.
filter()를 활용한 더 간결한 방법
ES6 이상 환경이라면 filter() 메서드와 전개 연산자(spread operator)를 조합해 훨씬 간결하게 표현할 수도 있습니다.
const difference = (first, second) => [
...first.filter(x => !second.includes(x)),
...second.filter(x => !first.includes(x))
];
console.log(difference(arr1, arr2)); // [ 6, 5, 1 ]
두 방식 모두 동일한 결과를 반환하지만, filter() 기반 코드는 반복문과 임시 변수 없이 의도를 명확하게 드러내므로 가독성 면에서 유리합니다. 다만 배열 크기가 매우 클 경우 성능을 고려해 Set 객체로 조회 시간을 줄이는 최적화도 가능합니다.