program tip

두 JavaScript 객체의 동등성을 결정하는 방법은 무엇입니까?

radiobox 2020. 10. 3. 10:23
반응형

두 JavaScript 객체의 동등성을 결정하는 방법은 무엇입니까?


완전 항등 연산자는 두 객체 유형 이 같은지 여부를 알려줍니다 . 그러나 Java 의 해시 코드과 같이 두 개체가 같은지 알 수있는 방법이 있습니까?

스택 오버플로 질문 JavaScript에 어떤 종류의 hashCode 함수가 있습니까? 이 질문과 비슷하지만 더 학문적 인 답변이 필요합니다. 위의 시나리오는 하나가 필요한 이유를 보여 주며 동등한 솔루션 이 있는지 궁금합니다 .


짧은 대답

간단한 대답은 다음과 같습니다. 아니오, 당신이 의미하는 의미에서 객체가 다른 객체와 동일하다는 것을 결정하는 일반적인 방법은 없습니다. 예외는 객체가 유형이 없다고 엄격하게 생각하는 경우입니다.

긴 대답

개념은 객체의 서로 다른 두 인스턴스를 비교하여 값 수준에서 동일한 지 여부를 나타내는 Equals 메서드의 개념입니다. 그러나 Equals메소드 구현 방법 을 정의하는 것은 특정 유형에 달려 있습니다. 기본 값을 가진 속성의 반복적 인 비교는 충분하지 않을 수 있으며 객체 값의 일부로 간주되지 않는 속성이있을 수 있습니다. 예를 들면

 function MyClass(a, b)
 {
     var c;
     this.getCLazy = function() {
         if (c === undefined) c = a * b // imagine * is really expensive
         return c;
     }
  }

이 위의 경우, cMyClass에의 두 인스턴스가 같은지 만 여부를 결정하기 위해 정말 중요하지 않습니다 ab중요하다. 경우에 c따라 인스턴스마다 다를 수 있지만 비교 중에 중요하지 않을 수 있습니다.

이 문제는 구성원 자체가 유형의 인스턴스 일 수 있고 이들 각각이 동등성을 결정하는 수단이 있어야하는 경우에 적용됩니다.

더욱 복잡한 것은 JavaScript에서 데이터와 방법의 구분이 모호하다는 것입니다.

객체는 이벤트 핸들러로 호출 될 메서드를 참조 할 수 있으며 이는 '값 상태'의 일부로 간주되지 않을 가능성이 높습니다. 다른 개체에는 중요한 계산을 수행하는 함수가 할당되어 다른 함수를 참조하기 때문에이 인스턴스를 다른 개체와 다르게 만드는 것이 좋습니다.

기존 프로토 타입 메서드 중 하나가 다른 함수에 의해 재정의 된 객체는 어떻습니까? 다른 인스턴스와 동일하다고 여전히 간주 될 수 있습니까? 이 질문은 각 유형에 대한 각 특정 사례에서만 답변 할 수 있습니다.

앞서 언급했듯이 예외는 엄격하게 유형이없는 객체입니다. 이 경우 유일한 현명한 선택은 각 멤버의 반복적이고 반복적 인 비교입니다. 그럼에도 불구하고 함수의 '가치'가 무엇인지 물어봐야합니다.


바퀴를 재발 명하는 이유는 무엇입니까? 부여 Lodash을 시도합니다. isEqual () 과 같은 필수 기능이 많이 있습니다 .

_.isEqual(object, other);

이 페이지의 다른 예제와 마찬가지로 ECMAScript 5 및 기본 최적화 (브라우저에서 사용할 수있는 경우)를 사용하여 각 키 값을 무차별 검사 합니다.

참고 : 이전에이 답변은 Underscore.js를 권장 했지만 lodash 는 버그를 수정하고 일관성있는 문제를 해결하는 더 나은 작업을 수행했습니다.


JavaScript for Objects의 기본 같음 연산자는 메모리에서 동일한 위치를 참조 할 때 true를 생성합니다.

var x = {};
var y = {};
var z = x;

x === y; // => false
x === z; // => true

다른 등식 연산자가 필요한 경우 equals(other)메서드 또는 이와 유사한 것을 클래스 에 추가해야하며 문제 도메인의 세부 사항에 따라 정확히 의미하는 바가 결정됩니다.

다음은 카드 게임의 예입니다.

function Card(rank, suit) {
  this.rank = rank;
  this.suit = suit;
  this.equals = function(other) {
     return other.rank == this.rank && other.suit == this.suit;
  };
}

var queenOfClubs = new Card(12, "C");
var kingOfSpades = new Card(13, "S");

queenOfClubs.equals(kingOfSpades); // => false
kingOfSpades.equals(new Card(13, "S")); // => true

AngularJS 에서 작업 angular.equals하는 경우 함수는 두 개체가 동일한 지 여부를 결정합니다. 에서 Ember.js의 사용 isEqual.

  • angular.equals- 이 방법에 대한 자세한 내용은 문서 또는 소스참조하십시오 . 배열에서도 심층 비교를 수행합니다.
  • Ember.js- 이 방법에 대한 자세한 isEqual내용은 문서 또는 소스참조하십시오 . 배열에 대한 심층 비교를 수행하지 않습니다.

var purple = [{"purple": "drank"}];
var drank = [{"purple": "drank"}];

if(angular.equals(purple, drank)) {
    document.write('got dat');
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>


이것은 내 버전입니다. ES5에 도입 된 새로운 Object.keys 기능과 + , ++의 아이디어 / 테스트를 사용 하고 있습니다 .

function objectEquals(x, y) {
    'use strict';

    if (x === null || x === undefined || y === null || y === undefined) { return x === y; }
    // after this just checking type of one would be enough
    if (x.constructor !== y.constructor) { return false; }
    // if they are functions, they should exactly refer to same one (because of closures)
    if (x instanceof Function) { return x === y; }
    // if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES)
    if (x instanceof RegExp) { return x === y; }
    if (x === y || x.valueOf() === y.valueOf()) { return true; }
    if (Array.isArray(x) && x.length !== y.length) { return false; }

    // if they are dates, they must had equal valueOf
    if (x instanceof Date) { return false; }

    // if they are strictly equal, they both need to be object at least
    if (!(x instanceof Object)) { return false; }
    if (!(y instanceof Object)) { return false; }

    // recursive object equality check
    var p = Object.keys(x);
    return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) &&
        p.every(function (i) { return objectEquals(x[i], y[i]); });
}


///////////////////////////////////////////////////////////////
/// The borrowed tests, run them by clicking "Run code snippet"
///////////////////////////////////////////////////////////////
var printResult = function (x) {
    if (x) { document.write('<div style="color: green;">Passed</div>'); }
    else { document.write('<div style="color: red;">Failed</div>'); }
};
var assert = { isTrue: function (x) { printResult(x); }, isFalse: function (x) { printResult(!x); } }
assert.isTrue(objectEquals(null,null));
assert.isFalse(objectEquals(null,undefined));
assert.isFalse(objectEquals(/abc/, /abc/));
assert.isFalse(objectEquals(/abc/, /123/));
var r = /abc/;
assert.isTrue(objectEquals(r, r));

assert.isTrue(objectEquals("hi","hi"));
assert.isTrue(objectEquals(5,5));
assert.isFalse(objectEquals(5,10));

assert.isTrue(objectEquals([],[]));
assert.isTrue(objectEquals([1,2],[1,2]));
assert.isFalse(objectEquals([1,2],[2,1]));
assert.isFalse(objectEquals([1,2],[1,2,3]));

assert.isTrue(objectEquals({},{}));
assert.isTrue(objectEquals({a:1,b:2},{a:1,b:2}));
assert.isTrue(objectEquals({a:1,b:2},{b:2,a:1}));
assert.isFalse(objectEquals({a:1,b:2},{a:1,b:3}));

assert.isTrue(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assert.isFalse(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));

Object.prototype.equals = function (obj) { return objectEquals(this, obj); };
var assertFalse = assert.isFalse,
    assertTrue = assert.isTrue;

assertFalse({}.equals(null));
assertFalse({}.equals(undefined));

assertTrue("hi".equals("hi"));
assertTrue(new Number(5).equals(5));
assertFalse(new Number(5).equals(10));
assertFalse(new Number(1).equals("1"));

assertTrue([].equals([]));
assertTrue([1,2].equals([1,2]));
assertFalse([1,2].equals([2,1]));
assertFalse([1,2].equals([1,2,3]));
assertTrue(new Date("2011-03-31").equals(new Date("2011-03-31")));
assertFalse(new Date("2011-03-31").equals(new Date("1970-01-01")));

assertTrue({}.equals({}));
assertTrue({a:1,b:2}.equals({a:1,b:2}));
assertTrue({a:1,b:2}.equals({b:2,a:1}));
assertFalse({a:1,b:2}.equals({a:1,b:3}));

assertTrue({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assertFalse({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));

var a = {a: 'text', b:[0,1]};
var b = {a: 'text', b:[0,1]};
var c = {a: 'text', b: 0};
var d = {a: 'text', b: false};
var e = {a: 'text', b:[1,0]};
var i = {
    a: 'text',
    c: {
        b: [1, 0]
    }
};
var j = {
    a: 'text',
    c: {
        b: [1, 0]
    }
};
var k = {a: 'text', b: null};
var l = {a: 'text', b: undefined};

assertTrue(a.equals(b));
assertFalse(a.equals(c));
assertFalse(c.equals(d));
assertFalse(a.equals(e));
assertTrue(i.equals(j));
assertFalse(d.equals(k));
assertFalse(k.equals(l));

// from comments on stackoverflow post
assert.isFalse(objectEquals([1, 2, undefined], [1, 2]));
assert.isFalse(objectEquals([1, 2, 3], { 0: 1, 1: 2, 2: 3 }));
assert.isFalse(objectEquals(new Date(1234), 1234));

// no two different function is equal really, they capture their context variables
// so even if they have same toString(), they won't have same functionality
var func = function (x) { return true; };
var func2 = function (x) { return true; };
assert.isTrue(objectEquals(func, func));
assert.isFalse(objectEquals(func, func2));
assert.isTrue(objectEquals({ a: { b: func } }, { a: { b: func } }));
assert.isFalse(objectEquals({ a: { b: func } }, { a: { b: func2 } }));


JSON 라이브러리를 사용하는 경우 각 객체를 JSON으로 인코딩 한 다음 결과 문자열이 같은지 비교할 수 있습니다.

var obj1={test:"value"};
var obj2={test:"value2"};

alert(JSON.encode(obj1)===JSON.encode(obj2));

참고 :이 답변은 많은 경우에서 작동하지만 여러 사람들이 의견에서 지적했듯이 다양한 이유로 문제가 있습니다. 거의 모든 경우에 더 강력한 솔루션을 찾고 싶을 것입니다.


짧은 기능 deepEqual구현 :

function deepEqual(x, y) {
  return (x && y && typeof x === 'object' && typeof y === 'object') ?
    (Object.keys(x).length === Object.keys(y).length) &&
      Object.keys(x).reduce(function(isEqual, key) {
        return isEqual && deepEqual(x[key], y[key]);
      }, true) : (x === y);
}

편집 : 버전 2, 지브의 제안 및 ES6 화살표 기능 사용 :

function deepEqual(x, y) {
  const ok = Object.keys, tx = typeof x, ty = typeof y;
  return x && y && tx === 'object' && tx === ty ? (
    ok(x).length === ok(y).length &&
      ok(x).every(key => deepEqual(x[key], y[key]))
  ) : (x === y);
}

딥 복사 기능이 편리하다면 다음 트릭을 사용하여 속성 순서를 일치시키면서 계속 사용할 수 JSON.stringify있습니다.

function equals(obj1, obj2) {
    function _equals(obj1, obj2) {
        return JSON.stringify(obj1)
            === JSON.stringify($.extend(true, {}, obj1, obj2));
    }
    return _equals(obj1, obj2) && _equals(obj2, obj1);
}

데모 : http://jsfiddle.net/CU3vb/3/

이론적 해석:

의 속성이 obj1하나씩 클론에 복사되므로 클론의 순서가 유지됩니다. 그리고의 속성이 obj2클론에 복사 될 때 이미 존재하는 속성 obj1은 단순히 덮어 쓰여 지기 때문에 클론 의 순서는 유지됩니다.


두 개체가 같은지 테스트하려고합니까? 즉 : 그들의 속성이 동일합니까?

이 경우 다음 상황을 눈치 채 셨을 것입니다.

var a = { foo : "bar" };
var b = { foo : "bar" };
alert (a == b ? "Equal" : "Not equal");
// "Not equal"

다음과 같이해야 할 수도 있습니다.

function objectEquals(obj1, obj2) {
    for (var i in obj1) {
        if (obj1.hasOwnProperty(i)) {
            if (!obj2.hasOwnProperty(i)) return false;
            if (obj1[i] != obj2[i]) return false;
        }
    }
    for (var i in obj2) {
        if (obj2.hasOwnProperty(i)) {
            if (!obj1.hasOwnProperty(i)) return false;
            if (obj1[i] != obj2[i]) return false;
        }
    }
    return true;
}

분명히 그 함수는 약간의 최적화와 심층 검사 (중첩 된 객체를 처리하기 위해 :)를 수행 할 수있는 기능을 수행 var a = { foo : { fu : "bar" } }할 수 있지만 아이디어를 얻습니다.

FOR가 지적했듯이, 당신은 이것을 당신의 목적에 맞게 조정해야 할 수도 있습니다. 예를 들면 : 다른 클래스들은 "equal"의 다른 정의를 가질 수 있습니다. 일반 객체로 작업하는 경우 위의 내용으로 충분할 MyClass.equals()수 있으며 그렇지 않으면 사용자 정의 함수가 갈 길일 수 있습니다.


Object, Array, String, Int 와 같은 모든 것을 비교하기위한 가장 간단 하고 논리적 인 솔루션 ...

JSON.stringify({a: val1}) === JSON.stringify({a: val2})

노트 :

  • 당신은 교체해야 val1하고 val2개체로
  • 객체의 경우 양쪽 객체 모두에 대해 재귀 적으로 정렬 (키 기준)해야합니다.

Node.js에서는 기본 require("assert").deepEqual. 추가 정보 : http://nodejs.org/api/assert.html

예를 들면 :

var assert = require("assert");
assert.deepEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError

오류를 반환하는 대신 true/ 를 반환하는 또 다른 예 false:

var assert = require("assert");

function deepEqual(a, b) {
    try {
      assert.deepEqual(a, b);
    } catch (error) {
      if (error.name === "AssertionError") {
        return false;
      }
      throw error;
    }
    return true;
};

comparable함수를 사용하여 JSON에 필적하는 객체의 복사본을 생성합니다.

var comparable = o => (typeof o != 'object' || !o)? o :
  Object.keys(o).sort().reduce((c, key) => (c[key] = comparable(o[key]), c), {});

// Demo:

var a = { a: 1, c: 4, b: [2, 3], d: { e: '5', f: null } };
var b = { b: [2, 3], c: 4, d: { f: null, e: '5' }, a: 1 };

console.log(JSON.stringify(comparable(a)));
console.log(JSON.stringify(comparable(b)));
console.log(JSON.stringify(comparable(a)) == JSON.stringify(comparable(b)));
<div id="div"></div>

테스트에 유용합니다 (대부분의 테스트 프레임 워크에는 is기능이 있습니다).

is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), 'x must match y');

차이가 발견되면 문자열이 기록되어 차이점을 확인할 수 있습니다.

x must match y
got      {"a":1,"b":{"0":2,"1":3},"c":7,"d":{"e":"5","f":null}},
expected {"a":1,"b":{"0":2,"1":3},"c":4,"d":{"e":"5","f":null}}.

다음은 기능 스타일 접근 방식을 사용하는 ES6 / ES2015의 솔루션입니다.

const typeOf = x => 
  ({}).toString
      .call(x)
      .match(/\[object (\w+)\]/)[1]

function areSimilar(a, b) {
  const everyKey = f => Object.keys(a).every(f)

  switch(typeOf(a)) {
    case 'Array':
      return a.length === b.length &&
        everyKey(k => areSimilar(a.sort()[k], b.sort()[k]));
    case 'Object':
      return Object.keys(a).length === Object.keys(b).length &&
        everyKey(k => areSimilar(a[k], b[k]));
    default:
      return a === b;
  }
}

여기에서 데모 가능


누군가 이와 비슷한 것을 게시했는지는 모르겠지만 여기에 객체 평등을 확인하기 위해 만든 함수가 있습니다.

function objectsAreEqual(a, b) {
  for (var prop in a) {
    if (a.hasOwnProperty(prop)) {
      if (b.hasOwnProperty(prop)) {
        if (typeof a[prop] === 'object') {
          if (!objectsAreEqual(a[prop], b[prop])) return false;
        } else {
          if (a[prop] !== b[prop]) return false;
        }
      } else {
        return false;
      }
    }
  }
  return true;
}

또한 재귀 적이므로 그것이 당신이 부르는 것이라면 깊은 평등을 확인할 수도 있습니다.


_.isEqual(obj1, obj2)underscore.js 라이브러리에서 사용할 수 있습니다 .

다음은 예입니다.

var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]};
var clone  = {name: 'moe', luckyNumbers: [13, 27, 34]};
stooge == clone;
=> false
_.isEqual(stooge, clone);
=> true

여기에서 공식 문서를 참조하십시오 : http://underscorejs.org/#isEqual


객체의 속성 순서가 변경되지 않는다고 가정합니다.

JSON.stringify () 는 성능 측면이 확실하지 않은 딥 및 비딥 두 유형의 객체에 대해 작동합니다.

var object1 = {
  key: "value"
};

var object2 = {
  key: "value"
};

var object3 = {
  key: "no value"
};

console.log('object1 and object2 are equal: ', JSON.stringify(object1) === JSON.stringify(object2));

console.log('object2 and object3 are equal: ', JSON.stringify(object2) === JSON.stringify(object3));


많은 사람들이 깨닫지 못하는이 문제에 대한 간단한 해결책은 JSON 문자열 (문자 당)을 정렬하는 것입니다. 일반적으로 여기에 언급 된 다른 솔루션보다 빠릅니다.

function areEqual(obj1, obj2) {
    var a = JSON.stringify(obj1), b = JSON.stringify(obj2);
    if (!a) a = '';
    if (!b) b = '';
    return (a.split('').sort().join('') == b.split('').sort().join(''));
}

이 방법에 대한 또 다른 유용한 점은 "replacer"함수를 JSON.stringify 함수 ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) 에 전달하여 비교를 필터링 할 수 있다는 것입니다. / stringify # Example_of_using_replacer_parameter ). 다음은 "derp"라는 이름의 모든 개체 키만 비교합니다.

function areEqual(obj1, obj2, filter) {
    var a = JSON.stringify(obj1, filter), b = JSON.stringify(obj2, filter);
    if (!a) a = '';
    if (!b) b = '';
    return (a.split('').sort().join('') == b.split('').sort().join(''));
}
var equal = areEqual(obj1, obj2, function(key, value) {
    return (key === 'derp') ? value : undefined;
});

NodeJS를 사용하는 사람들을 isDeepStrictEqual위해이를 달성 할 수있는 네이티브 Util 라이브러리에서 호출되는 편리한 메서드 가 있습니다.

const util = require('util');

const foo = {
  hey: "ho",
  lets: "go"
}

const bar = {
  hey: "ho",
  lets: "go"
}

foo == bar // false
util.isDeepStrictEqual(foo, bar) // true

https://nodejs.org/api/util.html#util_util_isdeepstrictequal_val1_val2


게시 된 것보다 더 일반적인 객체 비교 기능이 필요하여 다음과 같이 정리했습니다. 비평 감사합니다 ...

Object.prototype.equals = function(iObj) {
  if (this.constructor !== iObj.constructor)
    return false;
  var aMemberCount = 0;
  for (var a in this) {
    if (!this.hasOwnProperty(a))
      continue;
    if (typeof this[a] === 'object' && typeof iObj[a] === 'object' ? !this[a].equals(iObj[a]) : this[a] !== iObj[a])
      return false;
    ++aMemberCount;
  }
  for (var a in iObj)
    if (iObj.hasOwnProperty(a))
      --aMemberCount;
  return aMemberCount ? false : true;
}

JSON 객체를 비교하는 경우 https://github.com/mirek/node-rus-diff 를 사용할 수 있습니다.

npm install rus-diff

용법:

a = {foo:{bar:1}}
b = {foo:{bar:1}}
c = {foo:{bar:2}}

var rusDiff = require('rus-diff').rusDiff

console.log(rusDiff(a, b)) // -> false, meaning a and b are equal
console.log(rusDiff(a, c)) // -> { '$set': { 'foo.bar': 2 } }

두 개체가 다른 경우 MongoDB 호환 {$rename:{...}, $unset:{...}, $set:{...}}개체가 반환됩니다.


나는 같은 문제에 직면했고 내 자신의 해결책을 작성하기 위해 탈락했다. 그러나 Arrays와 Objects를 비교하고 싶기 때문에 일반적인 솔루션을 만들었습니다. 프로토 타입에 함수를 추가하기로 결정했지만 독립형 함수로 쉽게 다시 작성할 수 있습니다. 다음은 코드입니다.

Array.prototype.equals = Object.prototype.equals = function(b) {
    var ar = JSON.parse(JSON.stringify(b));
    var err = false;
    for(var key in this) {
        if(this.hasOwnProperty(key)) {
            var found = ar.find(this[key]);
            if(found > -1) {
                if(Object.prototype.toString.call(ar) === "[object Object]") {
                    delete ar[Object.keys(ar)[found]];
                }
                else {
                    ar.splice(found, 1);
                }
            }
            else {
                err = true;
                break;
            }
        }
    };
    if(Object.keys(ar).length > 0 || err) {
        return false;
    }
    return true;
}

Array.prototype.find = Object.prototype.find = function(v) {
    var f = -1;
    for(var i in this) {
        if(this.hasOwnProperty(i)) {
            if(Object.prototype.toString.call(this[i]) === "[object Array]" || Object.prototype.toString.call(this[i]) === "[object Object]") {
                if(this[i].equals(v)) {
                    f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
                }
            }
            else if(this[i] === v) {
                f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
            }
        }
    }
    return f;
}

이 알고리즘은 두 부분으로 나뉩니다. equals 함수 자체와 배열 / 객체에서 속성의 숫자 인덱스를 찾는 함수입니다. indexof는 숫자와 문자열 만 찾고 객체는 찾지 않기 때문에 find 함수 만 필요합니다.

다음과 같이 부를 수 있습니다.

({a: 1, b: "h"}).equals({a: 1, b: "h"});

이 함수는 true 또는 false를 반환합니다.이 경우 true입니다. 알고리즘은 매우 복잡한 객체를 비교할 수 있습니다.

({a: 1, b: "hello", c: ["w", "o", "r", "l", "d", {answer1: "should be", answer2: true}]}).equals({b: "hello", a: 1, c: ["w", "d", "o", "r", {answer1: "should be", answer2: true}, "l"]})

위의 예는 속성의 순서가 다른 경우에도 true를 반환합니다. 주의해야 할 작은 세부 사항 하나 :이 코드는 동일한 유형의 두 변수도 확인하므로 "3"은 3과 동일하지 않습니다.


일부 es6 기능을 사용하여 내 버전의 객체 비교를 제공하고 싶었습니다. 주문을 고려하지 않습니다. 모든 if / else를 삼항으로 변환 한 후 다음과 같이 왔습니다.

function areEqual(obj1, obj2) {

    return Object.keys(obj1).every(key => {

            return obj2.hasOwnProperty(key) ?
                typeof obj1[key] === 'object' ?
                    areEqual(obj1[key], obj2[key]) :
                obj1[key] === obj2[key] :
                false;

        }
    )
}

스파게티 코드 답변을 봅니다. 타사 라이브러리를 사용하지 않고 매우 쉽습니다.

먼저 키 이름을 기준으로 두 개체를 정렬합니다.

let objectOne = { hey, you }
let objectTwo = { you, hey }

// If you really wanted you could make this recursive for deep sort.
const sortObjectByKeyname = (objectToSort) => {
    return Object.keys(objectToSort).sort().reduce((r, k) => (r[k] = objectToSort[k], r), {});
}

let objectOne = sortObjectByKeyname(objectOne)
let objectTwo = sortObjectByKeyname(objectTwo)

그런 다음 단순히 문자열을 사용하여 비교하십시오.

JSON.stringify(objectOne) === JSON.stringify(objectTwo)

해싱 또는 직렬화에 대해 조언합니다 (JSON 솔루션이 제안한대로). 두 개체가 같은지 테스트해야하는 경우 같음의 의미를 정의해야합니다. 두 개체의 모든 데이터 멤버가 일치하거나 메모리 위치가 일치해야하거나 (두 변수가 메모리에서 동일한 개체를 참조 함을 의미), 각 개체의 데이터 멤버가 하나만 일치해야 할 수도 있습니다.

최근에 인스턴스가 생성 될 때마다 생성자가 새 ID (1부터 시작하여 1 씩 증가)를 생성하는 객체를 개발했습니다. 이 객체에는 해당 id 값을 다른 객체의 id 값과 비교하고 일치하는 경우 true를 반환하는 isEqual 함수가 있습니다.

이 경우 "같음"을 id 값이 일치한다는 의미로 정의했습니다. 각 인스턴스에 고유 한 ID가 있다는 점을 감안할 때 일치하는 개체도 동일한 메모리 위치를 차지한다는 아이디어를 적용하는 데 사용할 수 있습니다. 그럴 필요는 없지만.


두 개체가 모든 속성에 대해 모두 동일한 값을 갖고 있고 모든 중첩 개체 및 배열에 대해 재귀 적으로 두 개체를 동일하게 간주하는 것이 유용합니다. 또한 다음 두 개체가 동일하다고 생각합니다.

var a = {p1: 1};
var b = {p1: 1, p2: undefined};

마찬가지로 배열에는 "누락 된"요소와 정의되지 않은 요소가있을 수 있습니다. 나는 그것들도 똑같이 취급 할 것입니다.

var c = [1, 2];
var d = [1, 2, undefined];

이 평등 정의를 구현하는 함수 :

function isEqual(a, b) {
    if (a === b) {
        return true;
    }

    if (generalType(a) != generalType(b)) {
        return false;
    }

    if (a == b) {
        return true;
    }

    if (typeof a != 'object') {
        return false;
    }

    // null != {}
    if (a instanceof Object != b instanceof Object) {
        return false;
    }

    if (a instanceof Date || b instanceof Date) {
        if (a instanceof Date != b instanceof Date ||
            a.getTime() != b.getTime()) {
            return false;
        }
    }

    var allKeys = [].concat(keys(a), keys(b));
    uniqueArray(allKeys);

    for (var i = 0; i < allKeys.length; i++) {
        var prop = allKeys[i];
        if (!isEqual(a[prop], b[prop])) {
            return false;
        }
    }
    return true;
}

소스 코드 (도우미 함수, generalType 및 uniqueArray 포함) : Unit TestTest Runner 여기 .


이 함수에 대해 다음과 같은 가정을하고 있습니다.

  1. 비교하는 객체를 제어하고 기본 값만 있습니다 (예 : 중첩 된 객체, 함수 등이 아님).
  2. 브라우저에서 Object.keys 를 지원합니다 .

이것은 단순한 전략의 시연으로 취급되어야합니다.

/**
 * Checks the equality of two objects that contain primitive values. (ie. no nested objects, functions, etc.)
 * @param {Object} object1
 * @param {Object} object2
 * @param {Boolean} [order_matters] Affects the return value of unordered objects. (ex. {a:1, b:2} and {b:2, a:1}).
 * @returns {Boolean}
 */
function isEqual( object1, object2, order_matters ) {
    var keys1 = Object.keys(object1),
        keys2 = Object.keys(object2),
        i, key;

    // Test 1: Same number of elements
    if( keys1.length != keys2.length ) {
        return false;
    }

    // If order doesn't matter isEqual({a:2, b:1}, {b:1, a:2}) should return true.
    // keys1 = Object.keys({a:2, b:1}) = ["a","b"];
    // keys2 = Object.keys({b:1, a:2}) = ["b","a"];
    // This is why we are sorting keys1 and keys2.
    if( !order_matters ) {
        keys1.sort();
        keys2.sort();
    }

    // Test 2: Same keys
    for( i = 0; i < keys1.length; i++ ) {
        if( keys1[i] != keys2[i] ) {
            return false;
        }
    }

    // Test 3: Values
    for( i = 0; i < keys1.length; i++ ) {
        key = keys1[i];
        if( object1[key] != object2[key] ) {
            return false;
        }
    }

    return true;
}

이것은 위의 모든 항목에 대한 추가 사항이며 대체가 아닙니다. 추가 재귀 사례를 확인할 필요없이 빠른 얕은 비교 개체가 필요한 경우. 여기 샷이 있습니다.

1) 고유 속성 수의 동일성, 2) 키 이름의 동일성, 3) bCompareValues ​​== true 인 경우, 해당 속성 값 및 해당 유형의 동일성 (3 중 동일)

var shallowCompareObjects = function(o1, o2, bCompareValues) {
    var s, 
        n1 = 0,
        n2 = 0,
        b  = true;

    for (s in o1) { n1 ++; }
    for (s in o2) { 
        if (!o1.hasOwnProperty(s)) {
            b = false;
            break;
        }
        if (bCompareValues && o1[s] !== o2[s]) {
            b = false;
            break;
        }
        n2 ++;
    }
    return b && n1 == n2;
}

간단한 키 / 값 쌍 개체 인스턴스의 키를 비교하기 위해 다음을 사용합니다.

function compareKeys(r1, r2) {
    var nloops = 0, score = 0;
    for(k1 in r1) {
        for(k2 in r2) {
            nloops++;
            if(k1 == k2)
                score++; 
        }
    }
    return nloops == (score * score);
};

키가 비교되면 간단한 추가 for..in루프로 충분합니다.

복잡성은 O (N * N)이고 N은 키 개수입니다.

내가 정의한 개체가 1000 개 이상의 속성을 보유하지 않기를 바랍니다.


나는 이것이 조금 오래되었다는 것을 알고 있지만,이 문제에 대해 내가 생각 해낸 해결책을 추가하고 싶습니다. 나는 개체가 있었고 그 데이터가 언제 변경되었는지 알고 싶었습니다. "Object.observe와 유사한 것"과 내가 한 일은 다음과 같습니다.

function checkObjects(obj,obj2){
   var values = [];
   var keys = [];
   keys = Object.keys(obj);
   keys.forEach(function(key){
      values.push(key);
   });
   var values2 = [];
   var keys2 = [];
   keys2 = Object.keys(obj2);
   keys2.forEach(function(key){
      values2.push(key);
   });
   return (values == values2 && keys == keys2)
}

여기에서 복제 할 수 있으며 값과 키를 비교하기 위해 다른 배열 집합을 만들 수 있습니다. 이제 배열이기 때문에 매우 간단하며 객체의 크기가 다른 경우 false를 반환합니다.


작업에 반복적으로 사용하는 개인 라이브러리에서 꺼내기. 다음 함수는 관대 한 재귀 딥 이퀄로, 확인하지 않습니다.

  • 클래스 평등
  • 상속 된 값
  • 엄격한 평등 가치

나는 주로 이것을 사용하여 다양한 API 구현에 대해 동일한 응답을 받는지 확인합니다. 구현 차이 (예 : 문자열 대 숫자) 및 추가 null 값이 발생할 수 있습니다.

구현은 매우 간단하고 짧습니다 (모든 주석이 제거 된 경우).

/** Recursively check if both objects are equal in value
***
*** This function is designed to use multiple methods from most probable 
*** (and in most cases) valid, to the more regid and complex method.
***
*** One of the main principles behind the various check is that while
*** some of the simpler checks such as == or JSON may cause false negatives,
*** they do not cause false positives. As such they can be safely run first.
***
*** # !Important Note:
*** as this function is designed for simplified deep equal checks it is not designed
*** for the following
***
*** - Class equality, (ClassA().a = 1) maybe valid to (ClassB().b = 1)
*** - Inherited values, this actually ignores them
*** - Values being strictly equal, "1" is equal to 1 (see the basic equality check on this)
*** - Performance across all cases. This is designed for high performance on the
***   most probable cases of == / JSON equality. Consider bench testing, if you have
***   more 'complex' requirments
***
*** @param  objA : First object to compare
*** @param  objB : 2nd object to compare
*** @param  .... : Any other objects to compare
***
*** @returns true if all equals, or false if invalid
***
*** @license Copyright by eugene@picoded.com, 2012.
***          Licensed under the MIT license: http://opensource.org/licenses/MIT
**/
function simpleRecusiveDeepEqual(objA, objB) {
	// Multiple comparision check
	//--------------------------------------------
	var args = Array.prototype.slice.call(arguments);
	if(args.length > 2) {
		for(var a=1; a<args.length; ++a) {
			if(!simpleRecusiveDeepEqual(args[a-1], args[a])) {
				return false;
			}
		}
		return true;
	} else if(args.length < 2) {
		throw "simpleRecusiveDeepEqual, requires atleast 2 arguments";
	}
	
	// basic equality check,
	//--------------------------------------------
	// if this succed the 2 basic values is equal,
	// such as numbers and string.
	//
	// or its actually the same object pointer. Bam
	//
	// Note that if string and number strictly equal is required
	// change the equality from ==, to ===
	//
	if(objA == objB) {
		return true;
	}
	
	// If a value is a bsic type, and failed above. This fails
	var basicTypes = ["boolean", "number", "string"];
	if( basicTypes.indexOf(typeof objA) >= 0 || basicTypes.indexOf(typeof objB) >= 0 ) {
		return false;
	}
	
	// JSON equality check,
	//--------------------------------------------
	// this can fail, if the JSON stringify the objects in the wrong order
	// for example the following may fail, due to different string order:
	//
	// JSON.stringify( {a:1, b:2} ) == JSON.stringify( {b:2, a:1} )
	//
	if(JSON.stringify(objA) == JSON.stringify(objB)) {
		return true;
	}
	
	// Array equality check
	//--------------------------------------------
	// This is performed prior to iteration check,
	// Without this check the following would have been considered valid
	//
	// simpleRecusiveDeepEqual( { 0:1963 }, [1963] );
	//
	// Note that u may remove this segment if this is what is intended
	//
	if( Array.isArray(objA) ) {
		//objA is array, objB is not an array
		if( !Array.isArray(objB) ) {
			return false;
		}
	} else if( Array.isArray(objB) ) {
		//objA is not array, objB is an array
		return false;
	}
	
	// Nested values iteration
	//--------------------------------------------
	// Scan and iterate all the nested values, and check for non equal values recusively
	//
	// Note that this does not check against null equality, remove the various "!= null"
	// if this is required
	
	var i; //reuse var to iterate
	
	// Check objA values against objB
	for (i in objA) {
		//Protect against inherited properties
		if(objA.hasOwnProperty(i)) {
			if(objB.hasOwnProperty(i)) {
				// Check if deep equal is valid
				if(!simpleRecusiveDeepEqual( objA[i], objB[i] )) {
					return false;
				}
			} else if(objA[i] != null) {
				//ignore null values in objA, that objB does not have
				//else fails
				return false;
			}
		}
	}
	
	// Check if objB has additional values, that objA do not, fail if so
	for (i in objB) {
		if(objB.hasOwnProperty(i)) {
			if(objB[i] != null && !objA.hasOwnProperty(i)) {
				//ignore null values in objB, that objA does not have
				//else fails
				return false;
			}
		}
	}
	
	// End of all checks
	//--------------------------------------------
	// By reaching here, all iteration scans have been done.
	// and should have returned false if it failed
	return true;
}

// Sanity checking of simpleRecusiveDeepEqual
(function() {
	if(
		// Basic checks
		!simpleRecusiveDeepEqual({}, {}) ||
		!simpleRecusiveDeepEqual([], []) ||
		!simpleRecusiveDeepEqual(['a'], ['a']) ||
		// Not strict checks
		!simpleRecusiveDeepEqual("1", 1) ||
		// Multiple objects check
		!simpleRecusiveDeepEqual( { a:[1,2] }, { a:[1,2] }, { a:[1,2] } ) ||
		// Ensure distinction between array and object (the following should fail)
		simpleRecusiveDeepEqual( [1963], { 0:1963 } ) ||
		// Null strict checks
		simpleRecusiveDeepEqual( 0, null ) ||
		simpleRecusiveDeepEqual( "", null ) ||
		// Last "false" exists to make the various check above easy to comment in/out
		false
	) {
		alert("FATAL ERROR: simpleRecusiveDeepEqual failed basic checks");
	} else { 
		//added this last line, for SO snippet alert on success
		alert("simpleRecusiveDeepEqual: Passed all checks, Yays!");
	}
})();

참고 URL : https://stackoverflow.com/questions/201183/how-to-determine-equality-for-two-javascript-objects

반응형