web-dev-qa-db-ja.com

2つのJavaScriptオブジェクトが等しいかどうかを判断する方法

厳密な等価演算子は、2つのオブジェクト types が等しいかどうかを教えてくれます。しかし、2つのオブジェクトが等しいかどうかを判断する方法はありますか。 Javaのハッシュコード valueによく似ていますか?

Stack Overflow question JavaScriptにはどんな種類のhashCode関数がありますか?はこの質問に似ていますが、もっと学術的な答えが必要です。上のシナリオはなぜそれが必要なのかを示しています、そして 同等の解決策 があるかどうか疑問に思います。

542
user4903

短い答え

簡単な答えは次のとおりです。いいえ、ある意味でオブジェクトが別のオブジェクトと等しいと判断する一般的な方法はありません。例外は、オブジェクトが型なしであることを厳密に考えているときです。

長い答え

その概念は、オブジェクトの2つの異なるインスタンスを比較して、それらが値レベルで等しいかどうかを示すEqualsメソッドの概念です。しかし、Equalsメソッドがどのように実装されるべきかを定義するのは特定の型次第です。プリミティブ値を持つ属性の反復比較は十分ではないかもしれません、オブジェクト値の一部と見なされるべきではない属性があるかもしれません。例えば、

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

この場合、MyClassの2つのインスタンスが等しいかどうかを判断するのにcはそれほど重要ではなく、abだけが重要です。場合によっては、cはインスタンス間で異なる可能性がありますが、比較の際には意味がありません。

この問題は、メンバー自身が型のインスタンスでもあり、これらのメンバーすべてが同等性を判断する手段を持つ必要がある場合にも当てはまります。

さらに複雑なのは、JavaScriptではデータとメソッドの違いがぼやけていることです。

オブジェクトは、イベントハンドラとして呼び出されることになっているメソッドを参照することができ、これはその 'value state'の一部とは考えられないでしょう。別のオブジェクトには、重要な計算を実行する関数が割り当てられている可能性がありますが、それによって、このインスタンスが別の関数を参照するという理由だけで他のインスタンスとは異なります。

既存のプロトタイプメソッドの1つが別の関数によってオーバーライドされているオブジェクトについてはどうですか。それは他の点では他の点では同等であるとまだ考えられますか?その質問は、各タイプの特定のケースでのみ回答できます。

前述のように、例外は厳密に型なしのオブジェクトです。その場合、唯一の賢明な選択は、各メンバーの反復的で再帰的な比較です。それでも、関数の「価値」とは何かを尋ねなければなりませんか。

158
AnthonyWJones

なぜ車輪を作り直すのですか? Lodash を試してみてください。これには、 isEqual() などの必須機能がいくつかあります。

_.isEqual(object, other);

このページの他の例と同じように、 ECMAScript 5 およびブラウザで利用可能な場合はネイティブの最適化を使用して、各キーの値を強制的にチェックします。

注:以前はこの回答で Underscore.js が推奨されていましたが、 lodash はバグの修正と一貫性のある問題への対処というより優れた仕事をしました。

449
CoolAJ86

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
145
Daniel X Moore

AngularJS で作業している場合、angular.equals関数は2つのオブジェクトが等しいかどうかを判断します。 Ember.jsisEqualを使用します。

  • angular.equals - この方法についての詳細は docs または source を参照してください。配列についても詳細比較を行います。
  • Ember.js isEqual - このメソッドの詳細については docs または source を参照してください。配列の詳細比較は行いません。
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>
71
Troy Harvey

これは私のバージョンです。 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 } }));
58
Ebrahim Byagowi

JSONライブラリを使用している場合は、各オブジェクトをJSONとしてエンコードし、結果の文字列が等しいかどうかを比較できます。

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

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

注:この回答は多くの場合有効ですが、コメントで指摘しているように、さまざまな理由から問題があります。ほとんどすべての場合において、より堅牢なソリューションを見つけたいでしょう。

47
Joel Anair

短い機能的な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);
}
31
atmin

ディープコピー機能が便利な場合は、 still のようにプロパティの順序を一致させながら、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のプロパティは1つずつクローンにコピーされるので、クローン内のそれらの順序は保持されます。 obj2のプロパティがクローンにコピーされると、obj1にすでに存在するプロパティは単純に上書きされるため、クローン内のそれらの順序は保持されます。

20
Ateş Göral

2つのオブジェクトが等しいかどうかをテストしようとしていますか?すなわち:それらの特性は等しいですか?

この場合、おそらくこの状況に気付いているでしょう。

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が指摘したように、これをあなた自身の目的に適合させる必要があるかもしれません。例えば、異なるクラスは「等しい」の異なる定義を持っているかもしれません。単純なオブジェクトで作業している場合は、上記で十分かもしれません。そうでない場合は、カスタムMyClass.equals()関数を使用する方法があります。

18
nickf

すべてを比較するための最も単純な および logical の解決策 Object、Array、String、Int ...

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

注意:

  • あなたはあなたのオブジェクトでval1val2を置き換える必要があります
  • オブジェクトの場合は、両側のオブジェクトを再帰的に(キーで)ソートする必要があります。
17

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;
};
16
Rafael Xavier

私はこの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}}.
12
jib

Heresは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;
  }
}

ここで利用可能なデモ

9
Alan R. Soares

underscore.jsライブラリから_.isEqual(obj1, obj2)を使用できます。

これが一例です。

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

誰かがこれに似たものを投稿したかどうか私は知りませんが、これは私がオブジェクトの同等性をチェックするために作った関数です。

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;
}

また、それは再帰的なので、それがあなたがそれをあなたが呼んでいるものであるならば、それはまた深い均等性をチェックすることができます。

6
Zac

多くの人が気付いていないこの問題に対する簡単な解決策は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(''));
}

このメソッドに関するもう1つの便利な点は、「置換」関数を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;
});
5
th317erd

Babelなどで ES6 + を使用している場合は、Object.is(x, y)も使用できます。

参照: http://wiki.ecmascript.org/doku.php?id=harmony:egal#object.is_x_y

4
Daniel Li

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 } }

2つのオブジェクトが異なる場合は、MongoDB互換の{$rename:{...}, $unset:{...}, $set:{...}}ライクオブジェクトが返されます。

3
Mirek Rusin

私は(JSONの解決策が示唆するように)ハッシュやシリアライゼーションに対して忠告したいと思います。 2つのオブジェクトが等しいかどうかをテストする必要がある場合は、等しいという意味を定義する必要があります。両方のオブジェクトのすべてのデータメンバが一致しているか、メモリの場所が一致している(両方の変数がメモリ内の同じオブジェクトを参照している)か、各オブジェクトの1つのデータメンバだけが一致している必要があります。

私は最近、インスタンスが作成されるたびにコンストラクタが新しいID(1から始まり1ずつ増加する)を作成するオブジェクトを開発しました。このオブジェクトには、そのid値を別のオブジェクトのid値と比較し、一致する場合にtrueを返すisEqual関数があります。

その場合、id値が一致することを意味するものとして「等しい」と定義しました。各インスタンスが一意のIDを持つことを考えると、これは、一致するオブジェクトも同じメモリ位置を占有するという考えを強制するために使用できます。それは必要ありませんが。

3
Bernard Igiri

掲載されていたものより一般的なオブジェクト比較関数が必要なので、私は以下を作り上げました。批評を感謝します...

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;
}
3
Liam

私は同じ問題に直面し、私自身の解決策を書くことにしました。しかし、私はまた、配列とオブジェクトを比較したい、そしてその逆もまた同様にしたいので、私は一般的な解決策を作りました。プロトタイプに関数を追加することにしましたが、それらをスタンドアロン関数に簡単に書き換えることができます。これがコードです:

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;
}

このアルゴリズムは2つの部分に分けられます。 equals関数自体と、配列/オブジェクト内のプロパティの数値インデックスを見つけるための関数。 indexofは数字と文字列のみを検索し、オブジェクトは検索しないため、find関数のみが必要です。

このように呼ぶことができます。

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

この関数はtrueまたはfalseを返します。この場合はtrueです。アルゴリズムalsは非常に複雑なオブジェクト間の比較を可能にします。

({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を返します。このコードでは、同じ種類の2つの変数もチェックするため、「3」は3と同じではありません。

3
Sir_baaron

Es6の機能を利用して私のバージョンのオブジェクト比較に貢献したいだけでした。注文は考慮されません。すべてのif/elseを3進数に変換した後、次のようになりました。

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;

        }
    )
}
3
Egor Litvinchuk

2つのオブジェクトがすべてのプロパティに対して同じ値を持ち、すべてのネストされたオブジェクトと配列に対して再帰的に等しい場合、それらを等しいと見なすことが役立ちます。また、次の2つのオブジェクトが等しいと考えます。

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を含む): ユニットテスト そして テストランナーはこちら

2
mckoss

スパゲッティコードの回答があります。サードパーティのライブラリを使用せずに、これは非常に簡単です。

まず、2つのオブジェクトをそれらのキー名でキー順に並べ替えます。

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)
2
Oliver Dixon

これは上記すべての追加であり、代替ではありません。余分な再帰的なケースをチェックする必要なしに、オブジェクトを高速に浅比較する必要がある場合。これはショットです。

これは、次のものを比較します。1)自身のプロパティの数が等しい、2)キー名が等しい、3)bCompareValues == trueの場合、対応するプロパティ値とその型が等しい(トリプル等価)

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;
}
2
Lex

これは、入力が少なく、簡単なJSONデータ比較のために多くの場合に機能するstringifyトリックのバージョンです。

var obj1Fingerprint = JSON.stringify(obj1).replace(/\{|\}/g,'').split(',').sort().join(',');
var obj2Fingerprint = JSON.stringify(obj2).replace(/\{|\}/g,'').split(',').sort().join(',');
if ( obj1Fingerprint === obj2Fingerprint) { ... } else { ... }
2
EJW

NodeJSを使っている人のために、これを実現できるネイティブのUtilライブラリにisDeepStrictEqualという便利なメソッドがあります。

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

2
Vaelin

この関数では、次のように仮定しています。

  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;
}
2
Aldo Fregoso

私はこれが少し古いことを知っています、しかし私はこの問題のために思い付いた解決策を加えたいと思います。オブジェクトがあり、そのデータがいつ変更されたのかを知りたかったのです。 「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を返すからです。

2
inoabrian

単純なキーと値のペアのオブジェクトインスタンスのキーを比較するには、次のようにします。

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以上のプロパティを持つことができないことを願っています...

2
Hefeust CORTES

私の個人的な図書館から引き出す、私は私の仕事のために繰り返し使う。次の関数は緩やかな再帰的な深等価で、 はチェックしません

  • クラス平等
  • 継承値
  • 厳密な等価性

私は主にこれを使って、さまざまな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 [email protected], 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!");
        }
})();
2
PicoCreator

あなたが平等によって何を意味するかによります。したがって、クラスの開発者として、クラスの同等性を定義するのはあなた次第です。

2つのインスタンスがメモリ内の同じ場所を指している場合、それらが「等しい」と見なされるケースが1つありますが、それは必ずしも望んでいることとは限りません。たとえば、Personクラスがある場合、2つのPersonオブジェクトの姓、名、および社会保障番号が同じであれば、それらは「等しい」と見なすことができます(それらがメモリ内の異なる場所を指している場合でも)。

その一方で、2つのオブジェクトがそれぞれのメンバの値が同じであれば、それらが等しいとは単純に言えません。つまり、クラスごとに、どのメンバーがオブジェクトの「アイデンティティー」を構成するのかを定義し、適切な等価演算子(==演算子やEqualsメソッドのオーバーロードを介して)を開発するのはクラス開発者の責任です。

2つのオブジェクトが同じハッシュを持つ場合、それらが等しいと言うのは一方通行です。しかし、インスタンスごとにハッシュがどのように計算されるのか疑問に思う必要があります。上記のPersonの例に戻り、ハッシュがFirst Name、Last Name、およびSocial Security Numberの各フィールドの値を調べて計算された場合は、このシステムを使用できます。それに加えて、私たちはそれからハッシュ方法の品質に頼っています(それはそれ自体が大きなトピックですが、すべてのハッシュが等しく作成されるとは限らず、悪いハッシュ方法はmoreにつながる)この場合、誤った一致が返されます。

1
FOR

この問題に対する非常に簡単な修正方法があります。2つを比較すると、両方のオブジェクトに対してJSON.stringify()を実行するだけです。

1
ProgramKiddo

オブジェクト内のプロパティの順序は変更されないと仮定します。

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));
1
student

による。オブジェクト内のキーの順序が重要ではない場合、そして私はそのオブジェクトのプロトタイプを知る必要はありません。使うことはいつも仕事をする。

const object = {};
JSON.stringify(object) === "{}" will pass but {} === "{}" will not
1
Ndifreke Ekim

私はJavascriptの専門家ではありませんが、これを解決するための簡単な試みの1つです。私は3つのことを確認します。

  1. これはobjectであり、typeof nullnullであるためobjectでもありません。
  2. 2つのオブジェクトのプロパティ数が同じ場合そうでない場合、それらは等しくありません。
  3. 1つのプロパティをループ処理し、対応するプロパティが2番目のオブジェクトで同じ値を持つかどうかを確認します。
function deepEqual (first, second) {
  // Not equal if either is not an object or is null.
  if (!isObject(first) || !isObject(second) ) return false;

  // If properties count is different
  if (keys(first).length != keys(second).length) return false;

  // Return false if any property value is different.
  for(prop in first){
    if (first[prop] != second[prop]) return false;
  }
  return true;
}

// Checks if argument is an object and is not null
function isObject(obj) {
  return (typeof obj === "object" && obj != null);
}

// returns arrays of object keys
function keys (obj) {
  result = [];
  for(var key in obj){
    result.Push(key);
  }
  return result;
}

// Some test code
obj1 = {
  name: 'Singh',
  age: 20
}

obj2 = {
  age: 20,
  name: 'Singh'
}

obj3 = {
  name: 'Kaur',
  age: 19
}

console.log(deepEqual(obj1, obj2));
console.log(deepEqual(obj1, obj3));
1
PunjCoder

これは、オブジェクトの「値の等価性」をチェックするための非常に基本的な方法です。

var john = {
    occupation: "Web Developer",
    age: 25
};

var bobby = {
    occupation: "Web Developer",
    age: 25
};

function isEquivalent(a, b) {
    // Create arrays of property names

    var aProps = Object.getOwnPropertyNames(a);
    var bProps = Object.getOwnPropertyNames(b);

    // If number of properties is different, objects are not equivalent

    if (aProps.length != bProps.length) {
        return false;
    }

    for (var i = 0; i < aProps.length; i++) {
        var propName = aProps[i];

        // If values of same property are not equal, objects are not equivalent
        if (a[propName] !== b[propName]) {
           return false;
        }
    }

    // If we made it this far, objects are considered equivalent
    return true;
}

// Outputs: true
console.log(isEquivalent(john, bobby));

デモ - JSFiddle

ご覧のとおり、オブジェクトの「値の等価性」を確認するには、オブジェクト内のすべてのプロパティについて、それらが等しいかどうかを確認するために反復する必要があります。そして、この単純な実装は私たちの例のために働きますが、それが扱わない多くの場合があります。例えば:

  • プロパティ値の1つがそれ自体がオブジェクトの場合
  • プロパティ値の1つがNaN(JavaScriptのそれ自体と等しくない唯一の値)の場合はどうなりますか?
  • Aがundefinedの値を持つプロパティを持っていて、bがこのプロパティを持っていない(つまり、undefinedと評価される)場合、どうなりますか?

オブジェクトの「値の同等性」を確認するための堅牢な方法としては、 Underscore のようなさまざまなEdgeのケースをカバーする、十分にテストされたライブラリに頼るのが良いでしょう。

var john = {
    occupation: "Web Developer",
    age: 25
};

var bobby = {
    occupation: "Web Developer",
    age: 25
};

// Outputs: true
console.log(_.isEqual(john, bobby));

デモ - JSFiddle

1
Sudharshan
function isDeepEqual(obj1, obj2, testPrototypes = false) {
  if (obj1 === obj2) {
    return true
  }

  if (typeof obj1 === "function" && typeof obj2 === "function") {
    return obj1.toString() === obj2.toString()
  }

  if (obj1 instanceof Date && obj2 instanceof Date) {
    return obj1.getTime() === obj2.getTime()
  }

  if (
    Object.prototype.toString.call(obj1) !==
      Object.prototype.toString.call(obj2) ||
    typeof obj1 !== "object"
  ) {
    return false
  }

  const prototypesAreEqual = testPrototypes
    ? isDeepEqual(
        Object.getPrototypeOf(obj1),
        Object.getPrototypeOf(obj2),
        true
      )
    : true

  const obj1Props = Object.getOwnPropertyNames(obj1)
  const obj2Props = Object.getOwnPropertyNames(obj2)

  return (
    obj1Props.length === obj2Props.length &&
    prototypesAreEqual &&
    obj1Props.every(prop => isDeepEqual(obj1[prop], obj2[prop]))
  )
}

console.log(isDeepEqual({key: 'one'}, {key: 'first'}))
console.log(isDeepEqual({key: 'one'}, {key: 'one'}))
0

私はNode.jsとcompare.jsというブラウザで動作する小さなライブラリを書きました。 ==、!=、>、> =、<、<=などの通常の比較演算子と、JavaScriptのすべてのデータ型でのIDを提供します。

例えば、あなたは使用することができます

cmp.eq(obj1, obj2);

そしてこれは平等かどうかをチェックするでしょう(深い等しいアプローチを使って)。それ以外の場合は、

cmp.id(obj1, obj2);

それは参照によって比較されます、それ故にアイデンティティをチェックします。オブジェクトに<と>を使用することもできます。これはサブセットとスーパーセットを意味します。

compare.jsは約700の単体テストでカバーされているので、うまくいけばバグが多すぎないはずです;-)。

あなたはそれを見つけることができます https://github.com/goloroden/compare.js 無料で、それはMITライセンスの下でオープンソースです。

0
Golo Roden
function isEqual(obj1, obj2){
    type1 = typeof(obj1);
    type2 = typeof(obj2);
    if(type1===type2){
        switch (type1){
            case "object": return JSON.stringify(obj1)===JSON.stringify(obj2);
            case "function": return eval(obj1).toString()===eval(obj2).toString();
            default: return obj1==obj2;
        }
    }
    return false;
}//have not tried but should work.
0
Soyoes
     let user1 = {
        name: "John",
        address: {
            line1: "55 Green Park Road",
            line2: {
              a:[1,2,3]
            } 
        },
        email:null
        }
    
     let user2 = {
        name: "John",
        address: {
            line1: "55 Green Park Road",
            line2: {
              a:[1,2,3]
            } 
        },
        email:null
         }
    
    // Method 1
    
    function isEqual(a, b) {
          return JSON.stringify(a) === JSON.stringify(b);
    }
    
    // Method 2
    
    function isEqual(a, b) {
      // checking type of a And b
      if(typeof a !== 'object' || typeof b !== 'object') {
        return false;
      }
      
      // Both are NULL
      if(!a && !b ) {
         return true;
      } else if(!a || !b) {
         return false;
      }
      
      let keysA = Object.keys(a);
      let keysB = Object.keys(b);
      if(keysA.length !== keysB.length) {
        return false;
      }
      for(let key in a) {
       if(!(key in b)) {
         return false;
       }
        
       if(typeof a[key] === 'object') {
         if(!isEqual(a[key], b[key]))
           {
             return false;
           }
       } else {
         if(a[key] !== b[key]) {
           return false;
         }
       }
      }
      
      return true;
    }



console.log(isEqual(user1,user2));
0
chandan gupta

オブジェクト等価性検査:JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())

上記のテストは、オブジェクトの配列に対しても機能します。その場合、 http://www.w3schools.com/jsref/jsref_sort.asp に記載されているようにソート関数を使用します。

フラットなJSONスキーマを持つ小さな配列には十分かもしれません。

0
Sandeep

2つのオブジェクトが似ているかどうかを確認するための簡単な「ハック」は、それらのtoString()メソッドを使用することです。オブジェクトAとBをチェックしている場合は、AとBに意味のあるtoString()メソッドがあることを確認し、それらが返す文字列が同じであることを確認します。

これは万能薬ではありませんが、正しい状況では役に立つことがあります。

0
tmkly3

JQuery POST要求をモックする必要があるので、私にとって重要なのは、両方のオブジェクトが同じプロパティセットを持ち(どちらのオブジェクトにも存在しない)、各プロパティ値が「等しい」ことですこの定義に)。オブジェクトが一致しないメソッドを持っていても構いません。

これが私が使用することになるものです、それは 私の特定の 要件に十分であるべきです:

function PostRequest() {
    for (var i = 0; i < arguments.length; i += 2) {
        this[arguments[i]] = arguments[i+1];
    }

    var compare = function(u, v) {
        if (typeof(u) != typeof(v)) {
            return false;
        }

        var allkeys = {};
        for (var i in u) {
            allkeys[i] = 1;
        }
        for (var i in v) {
            allkeys[i] = 1;
        }
        for (var i in allkeys) {
            if (u.hasOwnProperty(i) != v.hasOwnProperty(i)) {
                if ((u.hasOwnProperty(i) && typeof(u[i]) == 'function') ||
                    (v.hasOwnProperty(i) && typeof(v[i]) == 'function')) {
                    continue;
                } else {
                    return false;
                }
            }
            if (typeof(u[i]) != typeof(v[i])) {
                return false;
            }
            if (typeof(u[i]) == 'object') {
                if (!compare(u[i], v[i])) {
                    return false;
                }
            } else {
                if (u[i] !== v[i]) {
                    return false;
                }
            }
        }

        return true;
    };

    this.equals = function(o) {
        return compare(this, o);
    };

    return this;
}

こんな感じで使う:

foo = new PostRequest('text', 'hello', 'html', '<p>hello</p>');
foo.equals({ html: '<p>hello</p>', text: 'hello' });
0

これは受け入れられますか?

deepEqual = (x, y) => {
 let areEqual = false;
 const Obj = Object.keys(x);
 const keysSize = Obj.length;
 let counter = 0;
 Obj.forEach(key => {
  if (y[key] === x[key]) {
    counter += 1;
  }
 });
 if (counter === keysSize) areEqual = true;
 return areEqual;
};
0
CoffeAbuser

私は2つのJSONを取り、それらのキーが再帰を使用して同じ値を持っているかどうかを確認するためにチェックするメソッドを実装しました。私は もう一つの質問 を使ってこれを解決しました。

const arraysEqual = (a, b) => {
    if (a === b)
        return true;
    if (a === null || b === null)
        return false;
    if (a.length !== b.length)
        return false;

    // If you don't care about the order of the elements inside
    // the array, you should sort both arrays here.

    for (let i = 0; i < a.length; ++i) {
        if (a[i] !== b[i])
            return false;
    }
    return true;
};

const jsonsEqual = (a, b) => {

  if(typeof a !== 'object' || typeof b !== 'object')
      return false;

  if (Object.keys(a).length === Object.keys(b).length) { // if items have the same size
      let response = true;

      for (let key in a) {
          if (!b[key]) // if not key
              response = false;
          if (typeof a[key] !== typeof b[key]) // if typeof doesn't equals
              response = false;
          else {
              if (Array.isArray(a[key])) // if array
                  response = arraysEqual(a[key], b[key]);
              else if (typeof a[key] === 'object') // if another json
                  response = jsonsEqual(a[key], b[key]);
              else if (a[key] !== b[key])  // not equals
                  response = false;
          }
          if (!response) // return if one item isn't equal
              return false;
      }
  } else
      return false;

  return true;
};

const json1 = { 
  a: 'a', 
  b: 'asd', 
  c: [
    '1',
    2,
    2.5,
    '3', 
    {
      d: 'asd',
      e: [
        1.6,
        { 
          f: 'asdasd',
          g: '123'
        }
      ]
    }
  ],
  h: 1,
  i: 1.2,
};

const json2 = {
  a: 'nops',
  b: 'asd'
};

const json3 = { 
  a: 'h', 
  b: '484', 
  c: [
    3,
    4.5,
    '2ss', 
    {
      e: [
        { 
          f: 'asdasd',
          g: '123'
        }
      ]
    }
  ],
  h: 1,
  i: 1.2,
};

const result = jsonsEqual(json1,json2);
//const result = jsonsEqual(json1,json3);
//const result = jsonsEqual(json1,json1);

if(result) // is equal
  $('#result').text("Jsons are the same")
else
  $('#result').text("Jsons aren't equals")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="result"></div>
0
gmonte

ええ、別の答え...

Object.prototype.equals = function (object) {
    if (this.constructor !== object.constructor) return false;
    if (Object.keys(this).length !== Object.keys(object).length) return false;
    var obk;
    for (obk in object) {
        if (this[obk] !== object[obk])
            return false;
    }
    return true;
}

var aaa = JSON.parse('{"name":"mike","tel":"1324356584"}');
var bbb = JSON.parse('{"tel":"1324356584","name":"mike"}');
var ccc = JSON.parse('{"name":"mike","tel":"584"}');
var ddd = JSON.parse('{"name":"mike","tel":"1324356584", "work":"nope"}');

$("#ab").text(aaa.equals(bbb));
$("#ba").text(bbb.equals(aaa));
$("#bc").text(bbb.equals(ccc));
$("#ad").text(aaa.equals(ddd));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
aaa equals bbb? <span id="ab"></span> <br/>
bbb equals aaa? <span id="ba"></span> <br/>
bbb equals ccc? <span id="bc"></span> <br/>
aaa equals ddd? <span id="ad"></span>
0
Tommaso Bertoni

私はすべてのサブオブジェクトまたは配列に深く入るであろうはるかに短い関数を持っています。これはJSON.stringify(obj1) === JSON.stringify(obj2)と同じくらい効率的ですが、順番が同じでない場合はJSON.stringifyは機能しません( ここで言及されているように )。

var obj1 = { a : 1, b : 2 };
var obj2 = { b : 2, a : 1 };

console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // false

あなたが等しくない値で何かをしたいのであれば、この関数も良いスタートです。

function arr_or_obj(v)
{ return !!v && (v.constructor === Object || v.constructor === Array); }

function deep_equal(v1, v2)
{
    if (arr_or_obj(v1) && arr_or_obj(v2) && v1.constructor === v2.constructor)
    {
        if (Object.keys(v1).length === Object.keys(v2).length) // check the length
        for (var i in v1)
        {
            if (!deep_equal(v1[i], v2[i]))
            { return false; }
        }
        else
        { return false; }
    }
    else if (v1 !== v2)
    { return false; }

    return true;
}

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////

var obj1 = [
    {
        hat : {
            cap : ['something', null ],
            helmet : [ 'triple eight', 'pro-tec' ]
        },
        shoes : [ 'loafer', 'penny' ]
    },
    {
        beers : [ 'budweiser', 'busch' ],
        wines : [ 'barefoot', 'yellow tail' ]
    }
];

var obj2 = [
    {
        shoes : [ 'loafer', 'penny' ], // same even if the order is different
        hat : {
            cap : ['something', null ],
            helmet : [ 'triple eight', 'pro-tec' ]
        }
    },
    {
        beers : [ 'budweiser', 'busch' ],
        wines : [ 'barefoot', 'yellow tail' ]
    }
];

console.log(deep_equal(obj1, obj2)); // true
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // false
console.log(deep_equal([], [])); // true
console.log(deep_equal({}, {})); // true
console.log(deep_equal([], {})); // false

FunctionDateRegExpのサポートを追加したい場合は、deep_equalの先頭に追加することができます(未テスト)。

if ((typeof obj1 === 'function' && typeof obj2 === 'function') ||
(obj1 instanceof Date && obj2 instanceof Date) ||
(obj1 instanceof RegExp && obj2 instanceof RegExp))
{
    obj1 = obj1.toString();
    obj2 = obj2.toString();
}
0
pmrotule

確かに、私たちがそれにたどり着いている間、私は私自身の車輪の再発明を投げ入れるでしょう(私はスポークと使用される材料の数を誇りに思います):

////////////////////////////////////////////////////////////////////////////////

var equals = function ( objectA, objectB ) {
    var result = false,
        keysA,
        keysB;

    // Check if they are pointing at the same variable. If they are, no need to test further.
    if ( objectA === objectB ) {
        return true;
    }

    // Check if they are the same type. If they are not, no need to test further.
    if ( typeof objectA !== typeof objectB ) {
        return false;
    }

    // Check what kind of variables they are to see what sort of comparison we should make.
    if ( typeof objectA === "object" ) {
        // Check if they have the same constructor, so that we are comparing apples with apples.
        if ( objectA.constructor === objectA.constructor ) {
            // If we are working with Arrays...
            if ( objectA instanceof Array ) {
                // Check the arrays are the same length. If not, they cannot be the same.
                if ( objectA.length === objectB.length ) {
                    // Compare each element. They must be identical. If not, the comparison stops immediately and returns false.
                    return objectA.every(
                        function ( element, i ) {
                            return equals( element, objectB[ i ] );
                        }
                    );
                }
                // They are not the same length, and so are not identical.
                else {
                    return false;
                }
            }
            // If we are working with RegExps...
            else if ( objectA instanceof RegExp ) {
                // Return the results of a string comparison of the expression.
                return ( objectA.toString() === objectB.toString() );
            }
            // Else we are working with other types of objects...
            else {
                // Get the keys as arrays from both objects. This uses Object.keys, so no old browsers here.
                keysA = Object.keys( objectA );

                keysB = Object.keys( objectB );

                // Check the key arrays are the same length. If not, they cannot be the same.
                if ( keysA.length === keysB.length ) {
                    // Compare each property. They must be identical. If not, the comparison stops immediately and returns false.
                    return keysA.every(
                        function ( element ) {
                            return equals( objectA[ element ], objectB[ element ] );
                        }
                    );
                }
                // They do not have the same number of keys, and so are not identical.
                else {
                    return false;
                }
            }
        }
        // They don't have the same constructor.
        else {
            return false;
        }
    }
    // If they are both functions, let us do a string comparison.
    else if ( typeof objectA === "function" ) {
        return ( objectA.toString() === objectB.toString() );
    }
    // If a simple variable type, compare directly without coercion.
    else {
        return ( objectA === objectB );
    }

    // Return a default if nothing has already been returned.
    return result;
};

////////////////////////////////////////////////////////////////////////////////

可能な限り迅速にfalseを返しますが、もちろん、違いが深くネストされている大きなオブジェクトの場合は、それほど効果的ではありません。私自身のシナリオでは、入れ子になった配列をうまく処理することが重要です。

このような「ホイール」を必要としている人に役立つことを願っています。

0
Kevin Teljeur

以下の解決策のうちのいくつかはパフォーマンス、機能性およびスタイルに問題があります...それらは十分に考え抜かれていません、そしてそれらのうちのいくつかはさまざまなケースで失敗します。私は自分自身の解決策でこの問題に対処しようとしました、そしてあなたのフィードバックに本当に感謝します。

http://stamat.wordpress.com/javascript-object-comparison/ /

//Returns the object's class, Array, Date, RegExp, Object are of interest to us
var getClass = function(val) {
    return Object.prototype.toString.call(val)
        .match(/^\[object\s(.*)\]$/)[1];
};

//Defines the type of the value, extended typeof
var whatis = function(val) {

    if (val === undefined)
        return 'undefined';
    if (val === null)
        return 'null';

    var type = typeof val;

    if (type === 'object')
        type = getClass(val).toLowerCase();

    if (type === 'number') {
        if (val.toString().indexOf('.') > 0)
            return 'float';
        else
        return 'integer';
    }

    return type;
   };

var compareObjects = function(a, b) {
    if (a === b)
        return true;
    for (var i in a) {
        if (b.hasOwnProperty(i)) {
            if (!equal(a[i],b[i])) return false;
        } else {
            return false;
        }
    }

    for (var i in b) {
        if (!a.hasOwnProperty(i)) {
            return false;
        }
    }
    return true;
};

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

var _equal = {};
_equal.array = compareArrays;
_equal.object = compareObjects;
_equal.date = function(a, b) {
    return a.getTime() === b.getTime();
};
_equal.regexp = function(a, b) {
    return a.toString() === b.toString();
};
//  uncoment to support function as string compare
//  _equal.fucntion =  _equal.regexp;



/*
 * Are two values equal, deep compare for objects and arrays.
 * @param a {any}
 * @param b {any}
 * @return {boolean} Are equal?
 */
var equal = function(a, b) {
    if (a !== b) {
        var atype = whatis(a), btype = whatis(b);

        if (atype === btype)
            return _equal.hasOwnProperty(atype) ? _equal[atype](a, b) : a==b;

        return false;
    }

    return true;
};
0
stamat

これは、入力として要素の配列を受け取り、それらを互いに比較する一般的な等価検査関数です。あらゆる種類の要素で動作します。

const isEqual = function(inputs = []) {
  // Checks an element if js object.
  const isObject = function(data) {
    return Object.prototype.toString.call(data) === '[object Object]';
  };
  // Sorts given object by its keys.
  const sortObjectByKey = function(obj) {
    const self = this;
    if (!obj) return {};
    return Object.keys(obj).sort().reduce((initialVal, item) => {
      initialVal[item] = !Array.isArray(obj[item]) &&
        typeof obj[item] === 'object'
        ? self.objectByKey(obj[item])
        : obj[item];
      return initialVal;
    }, {});
  };

  // Checks equality of all elements in the input against each other. Returns true | false
  return (
    inputs
      .map(
        input =>
          typeof input == 'undefined'
            ? ''
            : isObject(input)
                ? JSON.stringify(sortObjectByKey(input))
                : JSON.stringify(input)
      )
      .reduce(
        (prevValue, input) =>
          prevValue === '' || prevValue === input ? input : false,
        ''
      ) !== false
  );
};

// Tests (Made with Jest test framework.)
test('String equality check', () => {
  expect(isEqual(['murat'])).toEqual(true);
  expect(isEqual(['murat', 'john', 'doe'])).toEqual(false);
  expect(isEqual(['murat', 'murat', 'murat'])).toEqual(true);
});

test('Float equality check', () => {
  expect(isEqual([7.89, 3.45])).toEqual(false);
  expect(isEqual([7, 7.50])).toEqual(false);
  expect(isEqual([7.50, 7.50])).toEqual(true);
  expect(isEqual([7, 7])).toEqual(true);
  expect(isEqual([0.34, 0.33])).toEqual(false);
  expect(isEqual([0.33, 0.33])).toEqual(true);
});

test('Array equality check', () => {
  expect(isEqual([[1, 2, 3], [1, 2, 3]])).toEqual(true);
  expect(isEqual([[1, 3], [1, 2, 3]])).toEqual(false);
  expect(isEqual([['murat', 18], ['murat', 18]])).toEqual(true);
});

test('Object equality check', () => {
  let obj1 = {
    name: 'murat',
    age: 18
  };
  let obj2 = {
    name: 'murat',
    age: 18
  };
  let obj3 = {
    age: 18,
    name: 'murat'
  };
  let obj4 = {
    name: 'murat',
    age: 18,
    occupation: 'nothing'
  };
  expect(isEqual([obj1, obj2])).toEqual(true);
  expect(isEqual([obj1, obj2, obj3])).toEqual(true);
  expect(isEqual([obj1, obj2, obj3, obj4])).toEqual(false);
});

test('Weird equality checks', () => {
  expect(isEqual(['', {}])).toEqual(false);
  expect(isEqual([0, '0'])).toEqual(false);
});

要点もあります

0
muratgozel

これはとてもきれいな CoffeeScript のバージョンです。

Object::equals = (other) ->
  typeOf = Object::toString

  return false if typeOf.call(this) isnt typeOf.call(other)
  return `this == other` unless typeOf.call(other) is '[object Object]' or
                                typeOf.call(other) is '[object Array]'

  (return false unless this[key].equals other[key]) for key, value of this
  (return false if typeof this[key] is 'undefined') for key of other

  true

これがテストです。

  describe "equals", ->

    it "should consider two numbers to be equal", ->
      assert 5.equals(5)

    it "should consider two empty objects to be equal", ->
      assert {}.equals({})

    it "should consider two objects with one key to be equal", ->
      assert {a: "banana"}.equals {a: "banana"}

    it "should consider two objects with keys in different orders to be equal", ->
      assert {a: "banana", kendall: "garrus"}.equals {kendall: "garrus", a: "banana"}

    it "should consider two objects with nested objects to be equal", ->
      assert {a: {fruit: "banana"}}.equals {a: {fruit: "banana"}}

    it "should consider two objects with nested objects that are jumbled to be equal", ->
      assert {a: {a: "banana", kendall: "garrus"}}.equals {a: {kendall: "garrus", a: "banana"}}

    it "should consider two objects with arrays as values to be equal", ->
      assert {a: ["Apple", "banana"]}.equals {a: ["Apple", "banana"]}



    it "should not consider an object to be equal to null", ->
      assert !({a: "banana"}.equals null)

    it "should not consider two objects with different keys to be equal", ->
      assert !({a: "banana"}.equals {})

    it "should not consider two objects with different values to be equal", ->
      assert !({a: "banana"}.equals {a: "grapefruit"})
0
Luke

たくさんの良い考えがここにあります!これがディープイコールの私のバージョンです。私はそれをgithubに投稿し、その周りにいくつかのテストを書きました。考えられるすべてのケースを網羅するのは困難であり、時にはそれが不要な場合もあります。

私はNaN !== NaNと循環依存をカバーしました。

https://github.com/ryancat/simple-deep-equal/blob/master/index.js

0
Ryan.C

私のバージョンには、違いが見つかった場所と、違いが何であるかのチェーンが含まれています。

function DeepObjectCompare(O1, O2)
{
    try {
        DOC_Val(O1, O2, ['O1->O2', O1, O2]);
        return DOC_Val(O2, O1, ['O2->O1', O1, O2]);
    } catch(e) {
        console.log(e.Chain);
        throw(e);
    }
}
function DOC_Error(Reason, Chain, Val1, Val2)
{
    this.Reason=Reason;
    this.Chain=Chain;
    this.Val1=Val1;
    this.Val2=Val2;
}

function DOC_Val(Val1, Val2, Chain)
{
    function DoThrow(Reason, NewChain) { throw(new DOC_Error(Reason, NewChain!==undefined ? NewChain : Chain, Val1, Val2)); }

    if(typeof(Val1)!==typeof(Val2))
        return DoThrow('Type Mismatch');
    if(Val1===null || Val1===undefined)
        return Val1!==Val2 ? DoThrow('Null/undefined mismatch') : true;
    if(Val1.constructor!==Val2.constructor)
        return DoThrow('Constructor mismatch');
    switch(typeof(Val1))
    {
        case 'object':
            for(var m in Val1)
            {
                if(!Val1.hasOwnProperty(m))
                    continue;
                var CurChain=Chain.concat([m]);
                if(!Val2.hasOwnProperty(m))
                    return DoThrow('Val2 missing property', CurChain);
                DOC_Val(Val1[m], Val2[m], CurChain);
            }
            return true;
        case 'number':
            if(Number.isNaN(Val1))
                return !Number.isNaN(Val2) ? DoThrow('NaN mismatch') : true;
        case 'string':
        case 'boolean':
            return Val1!==Val2 ? DoThrow('Value mismatch') : true;
        case 'function':
            if(Val1.prototype!==Val2.prototype)
                return DoThrow('Prototype mismatch');
            if(Val1!==Val2)
                return DoThrow('Function mismatch');
            return true;
        default:
            return DoThrow('Val1 is unknown type');
    }
}
0
Dakusan

これがこの問題に対する私の解決策です。私は素晴らしいとは思わないが、それはあらゆるタイプとのオブジェクト比較で機能する

Object.prototype.fullMatch = function(obj){
    if (typeof this !== typeof obj) return false;
    if (this == null && obj != null || this != null && obj == null) return false;
    var this_keys = [];
    var obj_keys = [];
    for (var key in this) if (this.hasOwnProperty(key)) this_keys.Push(key);
    for (var key in obj) if (obj.hasOwnProperty(key)) obj_keys.Push(key);
    if (this_keys.length !== obj_keys.length){
        this_keys = null;
        obj_keys = null;
        return false;
    }
    var full_match = true;
    for (var key in this){
        if (this.hasOwnProperty(key) && obj.hasOwnProperty(key)){
            var this_value = this[key];
            var obj_value = obj[key];
            if (typeof this_value !== typeof obj_value || ("object" === typeof this_value && !this_value.fullMatch(obj_value)) || "object" !== typeof this_value && this_value !== obj_value){
                full_match = false;
                break;
            }
        }
    }
    return full_match;
};
0
Martin