object 2つのJavaScriptオブジェクトの等価性を判断する方法





15 Answers

なぜ車を再発明するのですか? Lodashを試してみてください。 これは、 Lodashような多数の必須関数を持っています。

_.isEqual(object, other);

ECMAScript 5とブラウザで使用可能なネイティブ最適化を使用して、このページの他の例と同様に、各キー値をブルートフォースでチェックします。

注:以前はこのアンサーはUnderscore.js推奨していましたが、 lodashはバグを修正して一貫性のある問題に対処する優れた仕事をしました。

javascript object equals hashcode

厳密な等価演算子は、2つのオブジェクトが等しいかどうかを示します。 しかし、Javaのハッシュコード値と同じように、 2つのオブジェクトが等しいかどうかを判断する方法はありますか?

question JavaScriptにhashCode関数がありますか? この質問に似ていますが、より学問的な答えが必要です。 上記のシナリオでは、なぜそれが必要なのかが示されています同等の解決策があるかどうかは疑問です。




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

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




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

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

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

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




ディープコピー機能が便利な場合は、次のトリックを使用してプロパティの順序を一致させながら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/ : http://jsfiddle.net/CU3vb/3/

理論的根拠:

obj1のプロパティは1つずつ複製にコピーされるため、複製の順序は保持されます。 obj2のプロパティがクローンにコピーされると、 obj1すでに存在するプロパティは単に上書きされるため、クローン内のそれらのオーダーは保持されます。




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" } } )を行うことができます。

あなたが指摘したように、あなた自身の目的のためにこれを適合させなければならないかもしれません。例えば、異なるクラスは "等しい"の異なる定義を持つかもしれません。 単純なオブジェクトで作業しているだけの場合は、上記の方法で十分です。そうしないと、カスタムMyClass.equals()関数が使用できます。




この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ます)。 例えば

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



多くの人が気づいていないこの問題の簡単な解決策は、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 ")に "replacer"関数を渡して比較をフィルタリングできることです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;
});



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

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

また、再帰的なので、それはあなたがそれを呼び出している場合は、深い平等をチェックすることができます。




私は、ハッシュやシリアル化(JSONソリューションが示唆しているように)に対してアドバイスしたいと思います。 2つのオブジェクトが等しいかどうかをテストする必要がある場合は、equalsの意味を定義する必要があります。 両方のオブジェクトのすべてのデータメンバーが一致しているか、またはメモリロケーションが一致している必要があります(両方の変数がメモリ内の同じオブジェクトを参照していることを意味します)。

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

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




JSONオブジェクトを比較する場合は、 https://github.com/mirek/node-rus-diffを使用でき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:{...}}オブジェクトが返されます。




ちょうど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;

        }
    )
}



2つのオブジェクトがすべてのプロパティですべて同じ値を持ち、すべてのネストされたオブジェクトと配列で再帰的に使用される場合、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を含む):Unit TestTest Runnerはここにあります




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

これは、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;
}



私はこれが少し古くなっていることを知っていますが、私はこの問題について思いついた解決策を追加したいと思います。オブジェクトがあり、データがいつ変更されたかを知りたいと思っていました。"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を返すため、非常に簡単です。




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



Related