실무에서 배열을 다루다 보면 다음과 같이 중복된 요소가 포함된 배열을 만나게 됩니다.
const arr = [1,1,2,2,3,4,4,5];
이번 글에서는 이런 배열을 입력받아, 원본 배열에서 단 한 번만 등장하는 요소들만 담고 있는 새로운 배열을 반환하는 JavaScript 함수를 작성해 보겠습니다. 위 예시 배열의 경우 1, 2, 4는 두 번씩 등장하므로 제외되고, 오직 3과 5만 결과에 남아야 합니다.
구현 코드
핵심 아이디어는 간단합니다. 어떤 요소가 유일한지 판별하려면 해당 요소가 처음 등장하는 위치(indexOf)와 마지막으로 등장하는 위치(lastIndexOf)를 비교하면 됩니다. 두 값이 같다면 그 요소는 배열 전체에서 한 번만 나타난다는 뜻입니다.
const arr = [1,1,2,2,3,4,4,5];
const extractUnique = arr => {
const res = [];
for(let i = 0; i < arr.length; i++){
// 처음 등장 위치와 마지막 등장 위치가 다르면 중복 요소 → 건너뜀
if(arr.lastIndexOf(arr[i]) !== arr.indexOf(arr[i])){
continue;
}
res.push(arr[i]);
}
return res;
};
console.log(extractUnique(arr));실행 결과
위 코드를 실행하면 콘솔에 다음과 같은 결과가 출력됩니다.
[ 3, 5 ]
동작 원리 살펴보기
코드의 흐름을 단계별로 정리하면 다음과 같습니다.
① 배열의 각 요소를 순회하면서 indexOf(arr[i])로 해당 값이 처음 나타나는 인덱스를 구합니다.
② lastIndexOf(arr[i])로 같은 값이 마지막에 나타나는 인덱스를 구합니다.
③ 두 인덱스가 서로 다르면 같은 값이 여러 번 등장했다는 의미이므로 continue로 건너뜁니다.
④ 두 인덱스가 일치하는 경우(= 유일한 값)만 결과 배열 res에 추가합니다.
filter()를 활용한 더 간결한 방법
같은 로직을 Array.prototype.filter() 메서드를 사용하면 훨씬 선언적으로 표현할 수 있습니다.
const extractUnique = arr =>
arr.filter(item => arr.indexOf(item) === arr.lastIndexOf(item));
console.log(extractUnique([1,1,2,2,3,4,4,5])); // [ 3, 5 ]두 방식 모두 시간 복잡도는 O(n²)입니다. 배열 크기가 매우 크다면 Map 객체로 각 요소의 등장 횟수를 먼저 세어 O(n)으로 최적화할 수 있으니 참고하세요.