본문 바로가기
dev/javascript

[javascript] 자바스크립트 배열 비교 방법 (compare two arrays)

by 최연탄 2022. 8. 26.
728x90
반응형

참고: https://www.30secondsofcode.org/articles/s/javascript-array-comparison

단순 비교

JavaScript에서 느슨하거나 엄격한 등가연산자(== 또는 ===)로 두개의 배열을 비교하면 대부분의 결과는 false가 됩니다. 심지어 두개의 배열이 동일한 항목을 동일한 순서로 가지고 있더라고 마찬가지입니다. 왜 이런 결과가 나오냐면 JavaScript에서 배열과 객체는 값을 비교하는게 아니라 객체의 주소를 비교하기 때문입니다. 이는 다음의 예제는 원하는 결과를 내주지 않는다는 말입니다.

const a = [1, 2, 3];
const b = [1, 2, 3];

a === b; // false

JSON.stringify

일반적인 해결 방법으로 많은 사람들이 JSON.stringify() 메소드를 사용합니다. 이는 각각의 배열을 serialize 하고 이 serialize된 두개의 문자열을 비교할 수 있게 해줍니다. 간단한 구현 방법은 다음과 같습니다.

const equals = (a, b) => JSON.stringify(a) === JSON.stringify(b);

const a = [1, 2, 3];
const b = [1, 2, 3];

equals(a, b); // true

그냥 보기에는 아주 좋아 보입니다. 짧고 간단하게 이해가능한 솔루션입니다. 하지만 이는 다음의 예제처럼 다른 값이 같은 문자열로 serialize되는 특정 사례에서 실패합니다.

const str = 'a';
const strObj = new String('a');
str === strObj; // false
equals([str], [strObj]); // true, should be false

null === undefined; // false
equals([null], [undefined]); // true, should be false

이런 경우가 흔하지는 않지만 나중에 이슈가 발생했을 때 디버깅하고 수정하기 아주 힘들어질 수 있습니다. 이러한 이유로 이 솔루션은 대부분의 경우에서 추천되지 않습니다.

더 나은 방법

두 배열의 길이와 Array.prototype.every() 메소드를 사용해 비교하는 더 나은 방법이 있습니다.

const equals = (a, b) =>
  a.length === b.length &&
  a.every((v, i) => v === b[i]);

const a = [1, 2, 3];
const b = [1, 2, 3];
const str = 'a';
const strObj = new String('a');

equals(a, b); // true
equals([str], [strObj]); // false
equals([null], [undefined]); // false

이 접근방법은 위에서 언급한 직렬화관련 이슈에 보호장치를 가집니다. 하지만 이는 중첩된 배열이나 객체에 대해서는 적용되지 않습니다. 이 문제까지 해결하려면 재귀적으로 반복확인을 해야합니다. 이런 중첩 문제나 다른 이슈들을 보완하는 해결책을 만드려면 equals snippet 까지 적용해야합니다.

순서없이 비교

마지막으로 각각의 배열내 항목의 순서가 중요하지 않고 오직 동일한 항목들이 존재하는지만이 중요한 경우가 있을 수 있습니다. 이런 경우는 Set과 Array.prototype.filter() 메소드를 사용하여 Set으로 만든 유일한 값을 루프돌게하여 각각의 배열에 같은 양의 항목이 있는지 확인하는 식으로 만들면 됩니다.

const equalsIgnoreOrder = (a, b) => {
  if (a.length !== b.length) return false;
  const uniqueValues = new Set([...a, ...b]);
  for (const v of uniqueValues) {
    const aCount = a.filter(e => e === v).length;
    const bCount = b.filter(e => e === v).length;
    if (aCount !== bCount) return false;
  }
  return true;
}

더 자세한 설명은 haveSameContents snippet을 확인하기 바랍니다.

반응형

댓글