845

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

スタック オーバーフローの質問JavaScript には任意の種類の hashCode 関数がありますか? この質問に似ていますが、より学術的な回答が必要です。上記のシナリオは、それが必要な理由を示しており、同等のソリューションがあるかどうか疑問に思っています。

4

78 に答える 78

631

なぜ車輪を再発明するのですか?ロダッシュを試してみてください。isEqual()など、いくつかの必須関数があります。

_.isEqual(object, other);

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

注: 以前、この回答はUnderscore.jsを推奨していましたが、lodashはバグを修正し、一貫性を持って問題に対処するというより良い仕事をしました。

于 2010-07-07T19:35:38.133 に答える
216

短い答え

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

長い答え

この概念は、オブジェクトの 2 つの異なるインスタンスを比較して値レベルで等しいかどうかを示す Equals メソッドの概念です。Equalsただし、メソッドの実装方法を定義するのは特定の型次第です。プリミティブ値を持つ属性の反復比較では不十分な場合があります。オブジェクトには、等価性に関係のない属性が含まれている場合があります。例えば、

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

上記の場合、cMyClass の 2 つのインスタンスが等しいかどうかを判断することはそれほど重要ではなく、重要であるだけaですb。場合cによっては、インスタンス間で異なる場合がありますが、比較時には重要ではありません。

この問題は、メンバー自体が型のインスタンスである可能性があり、これらのそれぞれが等しいかどうかを判断する手段を持つ必要がある場合に適用されることに注意してください。

さらに複雑なのは、JavaScript ではデータとメソッドの区別が曖昧なことです。

オブジェクトは、イベント ハンドラーとして呼び出されるメソッドを参照する場合がありますが、これはその「値の状態」の一部とは見なされない可能性があります。一方、別のオブジェクトには、重要な計算を実行する関数が割り当てられている可能性があり、そのため、別の関数を参照しているという理由だけで、このインスタンスを他のインスタンスと区別することができます。

既存のプロトタイプ メソッドの 1 つが別の関数によってオーバーライドされているオブジェクトについてはどうでしょうか。それ以外の場合は同一である別のインスタンスと同等であると見なすことができますか? その質問は、各タイプの特定のケースでのみ回答できます。

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

于 2008-10-14T14:48:47.440 に答える
183

オブジェクトに対する JavaScript のデフォルトの等価演算子は、メモリ内の同じ場所を参照する場合に 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
于 2009-05-20T03:48:14.083 に答える
80

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

于 2013-05-28T09:28:49.833 に答える
65

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

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

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

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

于 2008-10-14T14:11:01.400 に答える
62

短い機能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、jib の提案と 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);
}
于 2015-10-03T11:36:42.053 に答える
26

For those of you using Node, there is a convenient method called isDeepStrictEqual on the nativeutil library that can achieve this.

const util = require('util');

const obj1 = {
  foo: "bar",
  baz: [1, 2]
};

const obj2 = {
  foo: "bar",
  baz: [1, 2]
};


obj1 == obj2 // false
util.isDeepStrictEqual(obj1, obj2) // true

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

于 2019-01-22T17:31:57.440 に答える
25

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()関数を使用することをお勧めします。

于 2008-10-14T13:59:48.880 に答える
24

ディープ コピー機能が手元にある場合は、次のトリックを使用して、プロパティの順序を一致させながら使用できます。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/

根拠:

のプロパティはobj11 つずつクローンにコピーされるため、クローン内の順序は維持されます。また、 のプロパティがobj2クローンにコピーされると、 に既に存在するプロパティobj1は単純に上書きされるため、クローン内の順序は維持されます。

于 2012-06-14T19:20:48.377 に答える
15

この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}}.
于 2016-03-06T01:04:07.763 に答える
12

関数型スタイルのアプローチを使用した 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;
  }
}

ここで利用可能なデモ

于 2015-08-25T05:14:37.110 に答える
5

いくつかの 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;

        }
    )
}
于 2016-11-15T16:02:49.753 に答える
4

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

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

于 2018-05-17T07:32:52.230 に答える
3

投稿されたものよりも一般的なオブジェクト比較関数が必要なため、次のように作成しました。批評は高く評価されています...

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;
}
于 2010-05-27T12:00:59.313 に答える
3

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:{...}}ようなオブジェクトが返されます。

于 2014-03-11T17:44:49.887 に答える
3

私は同じ問題に直面し、独自のソリューションを作成することにしました。しかし、配列とオブジェクト、およびその逆も比較したいので、一般的なソリューションを作成しました。関数をプロトタイプに追加することにしましたが、スタンドアロン関数に簡単に書き直すことができます。コードは次のとおりです。

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 です。このアルゴリズムにより、非常に複雑なオブジェクト間の比較が可能になります。

({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 と同じではありません。

于 2016-09-13T17:21:36.540 に答える
2

この関数では、次の仮定を行っています。

  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;
}
于 2013-09-27T01:07:00.463 に答える
2

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

于 2015-02-11T20:37:46.807 に答える
2

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

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 個を超えるプロパティを保持しないことを願っています...

于 2013-11-29T11:34:44.827 に答える
2

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

これは以下を比較します: 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;
}
于 2013-12-21T01:09:08.197 に答える
2

(JSON ソリューションが示唆するように) ハッシュまたはシリアライゼーションに反対することをお勧めします。2 つのオブジェクトが等しいかどうかをテストする必要がある場合は、equals の意味を定義する必要があります。両方のオブジェクトのすべてのデータ メンバーが一致するか、メモリ位置が一致する必要がある (つまり、両方の変数がメモリ内の同じオブジェクトを参照する) か、または各オブジェクトの 1 つのデータ メンバーのみが一致する必要がある可能性があります。

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

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

于 2008-11-19T16:20:00.590 に答える
2

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 を含む): 単体テストテスト ランナーはこちら

于 2010-08-28T19:52:42.323 に答える
2

これはES6 +を使用したソリューションです

// this comparison would not work for function and symbol comparisons
// this would only work best for compared objects that do not belong to same address in memory
// Returns true if there is no difference, and false otherwise


export const isObjSame = (obj1, obj2) => {
    if (typeof obj1 !== "object" && obj1 !== obj2) {
        return false;
    }

    if (typeof obj1 !== "object" && typeof obj2 !== "object" && obj1 === obj2) {
        return true;
    }

    if (typeof obj1 === "object" && typeof obj2 === "object") {
        if (Array.isArray(obj1) && Array.isArray(obj2)) {
            if (obj1.length === obj2.length) {
                if (obj1.length === 0) {
                    return true;
                }
                const firstElemType = typeof obj1[0];

                if (typeof firstElemType !== "object") {
                    const confirmSameType = currentType =>
                        typeof currentType === firstElemType;

                    const checkObjOne = obj1.every(confirmSameType);
                    const checkObjTwo = obj2.every(confirmSameType);

                    if (checkObjOne && checkObjTwo) {
                        // they are primitves, we can therefore sort before and compare by index
                        // use number sort
                        // use alphabet sort
                        // use regular sort
                        if (firstElemType === "string") {
                            obj1.sort((a, b) => a.localeCompare(b));
                            obj2.sort((a, b) => a.localeCompare(b));
                        }
                        obj1.sort((a, b) => a - b);
                        obj2.sort((a, b) => a - b);

                        let equal = true;

                        obj1.map((element, index) => {
                            if (!isObjSame(element, obj2[index])) {
                                equal = false;
                            }
                        });

                        return equal;
                    }

                    if (
                        (checkObjOne && !checkObjTwo) ||
                        (!checkObjOne && checkObjTwo)
                    ) {
                        return false;
                    }

                    if (!checkObjOne && !checkObjTwo) {
                        for (let i = 0; i <= obj1.length; i++) {
                            const compareIt = isObjSame(obj1[i], obj2[i]);
                            if (!compareIt) {
                                return false;
                            }
                        }

                        return true;
                    }

                    // if()
                }
                const newValue = isObjSame(obj1, obj2);
                return newValue;
            } else {
                return false;
            }
        }

        if (!Array.isArray(obj1) && !Array.isArray(obj2)) {
            let equal = true;
            if (obj1 && obj2) {
                const allKeys1 = Array.from(Object.keys(obj1));
                const allKeys2 = Array.from(Object.keys(obj2));

                if (allKeys1.length === allKeys2.length) {
                    allKeys1.sort((a, b) => a - b);
                    allKeys2.sort((a, b) => a - b);

                    allKeys1.map((key, index) => {
                        if (
                            key.toLowerCase() !== allKeys2[index].toLowerCase()
                        ) {
                            equal = false;
                            return;
                        }

                        const confirmEquality = isObjSame(obj1[key], obj2[key]);

                        if (!confirmEquality) {
                            equal = confirmEquality;
                            return;
                        }
                    });
                }
            }

            return equal;

            // return false;
        }
    }
};
于 2021-03-19T11:06:56.427 に答える
1

私は Javascript の専門家ではありませんが、これを解決するための簡単な試みを 1 つ紹介します。次の 3 点をチェックします。

  1. であるobjectからではないnullことtypeof nullもあり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));

于 2015-07-24T19:54:48.870 に答える
1

この質問には十分な回答がありますが、1 つのアプローチが欠けています:toJSONインターフェイスです。

通常、オブジェクトを文字列化して比較します。これが最速の方法だからです。しかし、多くの場合、プロパティの順序が原因で、比較は false と見なされます。

const obj1 = {
  a: 1,
  b: 2,
  c: { 
    ca: 1,
    cb: 2
  }
}

const obj2 = {
  b: 2, // changed order with a
  a: 1,
  c: { 
    ca: 1,
    cb: 2
  }
}

JSON.stringify(obj1) === JSON.stringify(obj2) // false

aプロパティとの順序が異なるため、明らかにオブジェクトは異なると見なされbます。

これを解決するには、toJSONインターフェイスを実装し、確定的な出力を定義します。

const obj1 = {
  a: 1,
  b: 2,
  c: { 
    ca: 1,
    cb: 2
  },
  toJSON() {
    return {
      a: this.a,
      b: this.b,
      c: { 
        ca: this.c.ca,
        cb: this.c.ca
      }
    }
  }
}

const obj2 = {
  b: 2,
  a: 1,
  c: { 
    ca: 1,
    cb: 2
  },
  toJSON() {
    return {
      a: this.a,
      b: this.b,
      c: { 
        ca: this.c.ca,
        cb: this.c.ca
      }
    }
  }
}

JSON.stringify(obj1) === JSON.stringify(obj2) // true

出来上がり: と の文字列表現は同じと見なされますobj1obj2

ヒント

オブジェクトの直接生成にアクセスできない場合は、単純にtoJSON関数をアタッチできます。

obj1.toJSON = function() {
  return {
    a: this.a,
    b: this.b,
    c: { 
      ca: this.c.ca,
      cb: this.c.ca
    }
  }
}

obj2.toJSON = function() {
  return {
    a: this.a,
    b: this.b,
    c: { 
      ca: this.c.ca,
      cb: this.c.ca
    }
  }
}

JSON.stringify(obj1) === JSON.stringify(obj2) // true
于 2021-06-25T12:14:00.077 に答える
1

配列とオブジェクトの両方が明確な方法で比較されることを確認するために、このメソッドを作成しました。

これもうまくいくはずです!:)

public class Objects {
    /**
     * Checks whether a value is of type Object
     * @param value the value
     */
    public static isObject = (value: any): boolean => {
        return value === Object(value) && Object.prototype.toString.call(value) !== '[object Array]'
    }

    /**
     * Checks whether a value is of type Array
     * @param value the value
     */
    public static isArray = (value: any): boolean => {
        return Object.prototype.toString.call(value) === '[object Array]' && !Objects.isObject(value)
    }

    /**
     * Check whether two values are equal
     */
    public static isEqual = (objectA: any, objectB: any) => {
        // Objects
        if (Objects.isObject(objectA) && !Objects.isObject(objectB)) {
            return false
        }
        else if (!Objects.isObject(objectA) && Objects.isObject(objectB)) {
            return false
        }
        // Arrays
        else if (Objects.isArray(objectA) && !Objects.isArray(objectB)) {
            return false
        }
        else if (!Objects.isArray(objectA) && Objects.isArray(objectB)) {
            return false
        }
        // Primitives
        else if (!Objects.isArray(objectA) && !Objects.isObject(objectA)) {
            return objectA === objectB
        }
        // Object or array
        else {
            const compareObject = (objectA: any, objectB: any): boolean => {
                if (Object.keys(objectA).length !== Object.keys(objectB).length) return false

                for (const propertyName of Object.keys(objectA)) {
                    const valueA = objectA[propertyName]
                    const valueB = objectB[propertyName]

                    if (!Objects.isEqual(valueA, valueB)) {
                        return false
                    }
                }

                return true
            }
            const compareArray = (arrayA: any[], arrayB: any[]): boolean => {
                if (arrayA.length !== arrayB.length) return false

                for (const index in arrayA) {
                    const valueA = arrayA[index]
                    const valueB = arrayB[index]

                    if (!Objects.isEqual(valueA, valueB)) {
                        return false
                    }
                }

                return true
            }
            if (Objects.isObject(objectA)) {
                return compareObject(objectA, objectB)
            } else {
                return compareArray(objectA, objectB)
            }
        }
    }
}
于 2020-06-04T19:37:39.980 に答える
0

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

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

一方、各メンバーの値が同じである場合、2つのオブジェクトが等しいと単純に言うことはできません。これは、必要でない場合があるためです。言い換えると、クラスごとに、オブジェクトの「ID」を構成するメンバーを定義し、適切な等式演算子を開発するのはクラス開発者の責任です(==演算子またはEqualsメソッドのオーバーロードを介して)。

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

于 2008-10-14T13:54:31.993 に答える
0

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

これは万能薬ではありませんが、場合によっては役立つことがあります。

于 2013-02-20T18:44:46.700 に答える
0

JSON.stringify() の使用は常に信頼できるとは限りません。したがって、この方法はあなたの質問IMOに対する最良の解決策です

まず第一に、いいえ、オブジェクトが等しいことを決定する一般的な手段はありません!

しかし、 Shallow Equality Comparisonという概念があります。この概念を使用するのに役立つnpmライブラリがあります

const shallowequal = require('shallowequal');
 
const object = { 'user': 'fred' };
const other = { 'user': 'fred' };

// Referential Equality Comparison (`strict ===`)
object === other; // → false

// Shallow Equality Comparison
shallowequal(object, other); // → true

shallowEqual比較メソッドの作成方法については、こちらを参照してください。オープンソースのfbjsFacebook ライブラリからのものです。


浅い等値比較

shallowequal(obj1, obj2)

shallowEqualobj12 つの値 (つまりと)の間で浅い等値比較を実行してobj2、それらが等しいかどうかを判断します。

等値化は、指定された のキーを反復処理し、 と の間で厳密に等しくない値を持つキーがあるたびobj1に戻ることによって実行されます。それ以外の場合は、すべてのキーの値が厳密に等しい場合に戻ります。falseobj1obj2true

于 2022-01-22T18:33:47.650 に答える
0

1 つの追加オプションは、 Ramda ライブラリequalsの使用です。

const c = {a: 1, b: 2};
const d = {b: 2, a: 1};
R.equals(c, d); //=> true
于 2020-11-24T13:41:41.147 に答える
-1

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' });
于 2010-01-12T13:54:39.733 に答える
-1

オブジェクトの等価性チェック:JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())

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

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

于 2014-02-13T18:06:05.577 に答える
-1

すべてのサブオブジェクトまたは配列に深く入り込む、はるかに短い関数があります。と同じくらい効率的です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

FunctionDateおよびのサポートを追加する場合は、これを(テストされていません)RegExpの先頭に追加できます。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();
}
于 2016-07-29T14:24:13.070 に答える
-1

ええ、別の答え...

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>

于 2015-03-12T09:49:59.963 に答える
-1

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

たとえば、使用できます

cmp.eq(obj1, obj2);

これにより、等しいかどうかがチェックされます (deep-equal アプローチを使用)。それ以外の場合は、

cmp.id(obj1, obj2);

参照によって比較されるため、同一性をチェックします。オブジェクトで < と > を使用することもできます。これは、サブセットとスーパーセットを意味します。

compare.js は 700 近くの単体テストでカバーされているため、あまり多くのバグがないことを願っています ;-)。

https://github.com/goloroden/compare.jsで無料で見つけることができ、MIT ライセンスの下でオープンソース化されています。

于 2012-08-25T05:05:52.590 に答える
-1

これは古典的な JavaScript の質問です。比較から無視するプロパティを選択できる機能を備えた深いオブジェクトの等価性をチェックするメソッドを作成しました。引数は、比較する 2 つのオブジェクトと、無視する文字列化されたプロパティのオプションの配列です。

function isObjectEqual( o1, o2, ignorePropsArr=[]) {
    // Deep Clone objects
    let _obj1 = JSON.parse(JSON.stringify(o1)),
        _obj2 = JSON.parse(JSON.stringify(o2));
    // Remove props to ignore
    ignorePropsArr.map( p => { 
        eval('_obj1.'+p+' = _obj2.'+p+' = "IGNORED"');
    });
    // compare as strings
    let s1 = JSON.stringify(_obj1),
        s2 = JSON.stringify(_obj2);
    // return [s1==s2,s1,s2];
    return s1==s2;
}

// Objects 0 and 1 are exact equals
obj0 = { price: 66544.10, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}
obj1 = { price: 66544.10, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}
obj2 = { price: 66544.12, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}
obj3 = { price: 66544.13, RSIs: [0.000432334, 0.00046531], candles: {A: 541, B: 321, C: 4322}}
obj4 = { price: 66544.14, RSIs: [0.000432334, 0.00046530], candles: {A: 543, B: 321, C: 4322}}

isObjectEqual(obj0,obj1) // true
isObjectEqual(obj0,obj2) // false
isObjectEqual(obj0,obj2,['price']) // true
isObjectEqual(obj0,obj3,['price']) // false
isObjectEqual(obj0,obj3,['price','candles.A']) // true
isObjectEqual(obj0,obj4,['price','RSIs[1]'])   // true
于 2019-09-04T11:17:25.470 に答える
-3

これを行う方法のきれいな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"})
于 2013-05-28T15:28:43.953 に答える