문제 상황
두 개의 숫자 배열이 다음과 같이 주어졌다고 가정해 보겠습니다.
const arr1 = [12, 54, 2, 4, 6, 34, 3];
const arr2 = [54, 2, 5, 12, 4, 1, 3, 34];
두 배열 중 한쪽에만 존재하고 양쪽 모두에는 속하지 않는 요소, 즉 수학적으로 말하는 대칭 차집합(symmetric difference)에 해당하는 값들을 반환하는 JavaScript 함수를 작성해야 합니다.
위 예시에서 두 배열에 공통으로 포함된 숫자는 12, 54, 2, 4, 3, 34입니다. 따라서 최종 결과에는 첫 번째 배열에만 있는 6과 두 번째 배열에만 있는 5, 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 ]
동작 원리
이 함수의 핵심 로직은 다음과 같습니다.
indexOf()메서드는 특정 요소가 배열에 존재하지 않으면-1을 반환합니다. 이 특성을 이용해 두 배열 간의 포함 여부를 판별합니다.- 첫 번째 반복문에서는
first배열의 각 요소가second배열에 없는지 확인하고, 없다면 결과 배열에 추가합니다. - 두 번째 반복문에서는 반대로
second배열의 요소가first배열에 없는 경우를 찾아 추가합니다. - 최종적으로 두 배열의 교집합을 제외한 모든 고유 요소가 담긴 배열이 반환됩니다.
더 간결한 방법: filter 활용
filter()와 includes() 메서드를 조합하면 동일한 로직을 훨씬 간결하게 표현할 수 있습니다.
const difference = (first, second) =>
[...first.filter(el => !second.includes(el)),
...second.filter(el => !first.includes(el))];
console.log(difference(arr1, arr2)); // [ 6, 5, 1 ]
이 방식은 가독성이 뛰어나고 함수형 프로그래밍 스타일에 잘 어울립니다. 다만 배열의 크기가 매우 클 경우 includes()가 내부적으로 선형 탐색을 수행하기 때문에, Set 객체로 변환하여 조회 시간을 O(1)로 줄이는 최적화를 함께 고려해 보는 것이 좋습니다.