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

자바스크립트 배열에서 중복되지 않는 요소의 합계 구하기

다음과 같은 숫자 배열이 있다고 가정해 보겠습니다.

const arr = [14, 54, 23, 14, 24, 33, 44, 54, 77, 87, 77, 14];

우리가 작성해야 할 것은 이 배열에서 딱 한 번만 등장하는 요소들만 골라내어 그 합계를 반환하는 자바스크립트 함수입니다.

문제 이해하기

위 배열에서 14, 54, 77은 각각 두 번씩 등장하는 중복 요소입니다. 반면 23, 24, 33, 44, 87은 한 번만 등장하므로, 이 값들의 합이 최종 결과가 됩니다.

예상 출력 결과

211

방법 1: indexOf()와 lastIndexOf()로 중복 확인하기

특정 값이 배열에 한 번만 존재하는지 확인하는 가장 직관적인 방법은, 그 값이 처음 등장하는 인덱스(indexOf())와 마지막에 등장하는 인덱스(lastIndexOf())를 비교하는 것입니다. 두 인덱스가 같다면 해당 요소는 배열에 단 하나뿐이라는 의미입니다.

const arr = [14, 54, 23, 14, 24, 33, 44, 54, 77, 87, 77, 14];

const nonRepeatingSum = arr => {
  let res = 0;
  for (let i = 0; i < arr.length; i++) {
    // 처음 등장 인덱스와 마지막 등장 인덱스가 다르면 중복 요소이므로 건너뜀
    if (arr.indexOf(arr[i]) !== arr.lastIndexOf(arr[i])) {
      continue;
    }
    res += arr[i];
  }
  return res;
};

console.log(nonRepeatingSum(arr)); // 211

콘솔에는 다음과 같이 출력됩니다.

211

방법 2: filter()와 reduce()로 더 간결하게

같은 로직을 함수형 스타일로 표현하면 코드가 훨씬 짧아집니다. 먼저 filter()로 한 번만 등장하는 요소만 남긴 뒤, reduce()로 합산합니다.

const nonRepeatingSum = arr =>
  arr
    .filter(num => arr.indexOf(num) === arr.lastIndexOf(num))
    .reduce((sum, num) => sum + num, 0);

console.log(nonRepeatingSum(arr)); // 211

방법 3: Map을 활용해 O(n)으로 최적화하기

indexOf()lastIndexOf()는 호출될 때마다 배열을 처음부터 훑기 때문에 앞선 방식들의 시간 복잡도는 O(n²)입니다. 배열이 매우 크다면 Map으로 각 값의 등장 횟수를 한 번의 순회로 세고, 다시 한 번 순회하며 등장 횟수가 1인 값만 더하는 방식이 훨씬 효율적입니다.

const nonRepeatingSum = arr => {
  const countMap = new Map();
  for (const num of arr) {
    countMap.set(num, (countMap.get(num) || 0) + 1);
  }

  let res = 0;
  for (const [num, count] of countMap) {
    if (count === 1) {
      res += num;
    }
  }
  return res;
};

console.log(nonRepeatingSum(arr)); // 211

참고: '중복 제거 후 고유 값의 합'과는 다릅니다

목표가 '한 번만 등장하는 요소의 합'이 아니라 중복을 제거한 고유 값들을 각각 한 번씩만 더하는 것이라면 결과가 달라집니다. 위 예제 배열의 고유 값은 14, 54, 23, 24, 33, 44, 77, 87 여덟 개이며, 이들의 합은 다음과 같습니다.

356

이 경우에는 Set을 활용하면 간단하게 해결할 수 있습니다.

const distinctSum = [...new Set(arr)].reduce((sum, num) => sum + num, 0);
console.log(distinctSum); // 356

마무리

정리하면, 배열에서 한 번만 등장하는 요소의 합을 구할 때는 indexOf()lastIndexOf()를 비교하는 방법이 가장 이해하기 쉽고, filter()reduce() 조합은 코드를 간결하게 만들어 주며, 대용량 데이터에서는 Map 기반의 O(n) 접근 방식이 가장 효율적입니다. 문제의 요구 사항과 데이터 크기에 맞는 방법을 선택해 적용하면 됩니다.