JS에서 객체를 비교하는 방법

Posted by yunki kim on July 24, 2021

  JS에서 primitive값에 대한 비교는 단순히 '==' 또는 '==='를 사용하면 된다. 하지만 객체의 경우, 객체는 여러 데이터로 구조화된 형태 이기 떄문에 객체의 비교는 방법이 다양하고 해당 방법들은 각기 다른 결과를 출력한다.

Referential equality

  JS는 다음과 같은 3가지 방식으로 값을 비교할 수 있다.

    1. ===

    2. ==

    3. Object.is()

  위 3개의 연산자는 모두 비교의 대상이 되는 값이 같은 인스턴스를 참조할때 참을 반환한다. 그때문에 Referential equality는 객체 레퍼런스를 비교할때 유용하다. 하지만 통상적인 상황에서는 레퍼런스가 아닌 실제 값을 비교해야 하는 상황이 더 많다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const hero1 = {
  name'Batman'
};
const hero2 = {
  name'Batman'
};
 
hero1 === hero1; // => true
hero1 === hero2; // => false
 
hero1 == hero1; // => true
hero1 == hero2; // => false
 
Object.is(hero1, hero1); // => true
Object.is(hero1, hero2); // => false
cs

Manual comparison

 객체 내부의 값을 비교하는 방식이다. 이 방식은 간단한 객체에 대해서는 괜찮은 방식이지만 복잡한 객체를 비교하기에는 부적절하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function isHeroEqual(object1, object2) {
  return object1.name === object2.name;
}
 
const hero1 = {
  name'Batman'
};
const hero2 = {
  name'Batman'
};
const hero3 = {
  name'Joker'
};
 
isHeroEqual(hero1, hero2); // => true
isHeroEqual(hero1, hero3); // => false
 
cs

Shallow equality

 키를통해 객체를 순회하면서 값을 체크하는 방식이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function shallowEqual(object1, object2) {
  const keys1 = Object.keys(object1);
  const keys2 = Object.keys(object2);
 
  if (keys1.length !== keys2.length) {
    return false;
  }
 
  for (let key of keys1) {
    if (object1[key] !== object2[key]) {
      return false;
    }
  }
 
  return true;
}
 
const hero1 = {
  name'Batman',
  realName: 'Bruce Wayne'
};
const hero2 = {
  name'Batman',
  realName: 'Bruce Wayne'
};
const hero3 = {
  name'Joker'
};
 
shallowEqual(hero1, hero2); // => true
shallowEqual(hero1, hero3); // => false
cs

  이 방식은 객체의 값들이 모두 primitive이면 사용해도 되는 방식이지만, 객체를 값으로 가지고 있다면 사용하면 안된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const hero1 = {
  name'Batman',
  address: {
    city: 'Gotham'
  }
};
const hero2 = {
  name'Batman',
  address: {
    city: 'Gotham'
  }
};
 
shallowEqual(hero1, hero2); // => false
cs

위 예제의 경우 두 객체에 들어 있는 값은 같지만 두 개의 address객체는 서로 다른 인스턴스 임으로 false가 반환 된다.

Deep equality

 shallow equality에서와 같이 객체가 중첩되어진 형태로 인해 문제가 생긴다면 deep equality를 사용하면 된다. Deep equality는 원칙적으로는 shallow equality와 같지만 비교를 하는 도중 객체를 만났을때 shallow equality를 재귀호출 한다는 점이 다르다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function deepEqual(object1, object2) {
  const keys1 = Object.keys(object1);
  const keys2 = Object.keys(object2);
 
  if (keys1.length !== keys2.length) {
    return false;
  }
 
  for (const key of keys1) {
    const val1 = object1[key];
    const val2 = object2[key];
    const areObjects = isObject(val1) && isObject(val2);
    if (
      areObjects && !deepEqual(val1, val2) ||
      !areObjects && val1 !== val2
    ) {
      return false;
    }
  }
 
  return true;
}
 
function isObject(object) {
  return object != null && typeof object === 'object';
}
 
const hero1 = {
  name'Batman',
  address: {
    city: 'Gotham'
  }
};
const hero2 = {
  name'Batman',
  address: {
    city: 'Gotham'
  }
};
 
deepEqual(hero1, hero2); // => true
 
cs

Deep equality의 경우 다음과 같은 메서드를 사용해 해결할 수 있다.

util.isDeepStrictEqual(v1, v2):https://nodejs.org/api/util.html#util_util_isdeepstrictequal_val1_val2

 

Util | Node.js v16.5.0 Documentation

Util# Source Code: lib/util.js The util module supports the needs of Node.js internal APIs. Many of the utilities are useful for application and module developers as well. To access it: const util = require('util'); util.callbackify(original)# Added in: v8

nodejs.org

_.isEqual(value, other):https://lodash.com/docs/4.17.15#isEqual