Computer >> 컴퓨터 >  >> 프로그래밍 >> JavaScript

JavaScript 배열에서 중복 값만 한 번씩 추출하는 방법

실무에서 배열을 다루다 보면 같은 값이 여러 번 반복해서 등장하는 경우가 자주 있습니다. 예를 들어 다음과 같은 숫자 배열이 있다고 가정해 보겠습니다.

const arr = [1, 4, 3, 3, 1, 3, 2, 4, 2, 1, 4, 4];

여기서 우리가 만들 함수의 목표는 명확합니다. 원본 배열에서 두 번 이상 등장한 값(중복 값)만 골라내되, 결과 배열에는 각 값을 딱 한 번만 포함하는 것입니다.

위 배열의 경우 1, 4, 3, 2 네 개의 값이 모두 여러 번 나타나므로, 기대하는 출력 결과는 다음과 같습니다.

const output = [1, 4, 3, 2];

해결 아이디어

핵심 로직은 간단합니다. 배열의 각 요소에 대해 다음 두 가지를 확인하면 됩니다.

  • 중복 여부 판별: indexOf()lastIndexOf()의 반환값이 서로 다르다면, 해당 값이 배열 안에 최소 두 번 이상 존재한다는 의미입니다.
  • 결과 중복 방지: 이미 결과 배열에 추가된 값인지 includes()로 검사하여, 같은 값이 여러 번 push 되지 않도록 합니다.

코드 구현

위 아이디어를 바탕으로 작성한 코드는 다음과 같습니다.

const arr = [1, 4, 3, 3, 1, 3, 2, 4, 2, 1, 4, 4];

const pickDuplicate = arr => {
    const res = [];
    for(let i = 0; i < arr.length; i++){
        // indexOf와 lastIndexOf가 다르면 중복 값
        if(arr.indexOf(arr[i]) !== arr.lastIndexOf(arr[i])){
            // 아직 결과에 없는 값만 추가
            if(!res.includes(arr[i])){
                res.push(arr[i]);
            }
        }
    }
    return res;
};

console.log(pickDuplicate(arr));

실행 결과

콘솔에 출력되는 결과는 다음과 같습니다.

[1, 4, 3, 2]

동작 원리 살펴보기

코드가 어떻게 동작하는지 단계별로 정리하면 다음과 같습니다.

  1. 배열을 처음부터 끝까지 순회하면서 각 요소를 하나씩 확인합니다.
  2. arr.indexOf(arr[i])는 해당 값이 처음 등장하는 위치를, arr.lastIndexOf(arr[i])마지막으로 등장하는 위치를 반환합니다.
  3. 두 위치가 다르면 그 값은 배열 내에서 최소 두 번 이상 등장한 중복 값입니다.
  4. 중복 값이라면, res.includes()로 이미 결과 배열에 담겼는지 확인한 뒤 없을 때만 추가합니다.

참고: Set을 활용한 대안

ES6의 Set 객체를 활용하면 코드를 더 간결하게 만들 수 있습니다.

const pickDuplicate = arr => {
    const seen = new Set();
    const res = new Set();
    
    for(const num of arr){
        if(seen.has(num)){
            res.add(num);
        } else {
            seen.add(num);
        }
    }
    return [...res];
};

console.log(pickDuplicate([1, 4, 3, 3, 1, 3, 2, 4, 2, 1, 4, 4]));
// [1, 4, 3, 2]

이 방식은 Set의 조회 성능이 O(1)이기 때문에, indexOf/includes를 사용하는 첫 번째 방법(O(n²))보다 배열이 클 때 훨씬 효율적입니다. 상황에 맞게 적절한 방법을 선택하시기 바랍니다.