厳密な等価演算子は、2つのオブジェクト types が等しいかどうかを教えてくれます。しかし、2つのオブジェクトが等しいかどうかを判断する方法はありますか。 Javaのハッシュコード valueによく似ていますか?
Stack Overflow question JavaScriptにはどんな種類のhashCode関数がありますか?はこの質問に似ていますが、もっと学術的な答えが必要です。上のシナリオはなぜそれが必要なのかを示しています、そして 同等の解決策 があるかどうか疑問に思います。
短い答え
簡単な答えは次のとおりです。いいえ、ある意味でオブジェクトが別のオブジェクトと等しいと判断する一般的な方法はありません。例外は、オブジェクトが型なしであることを厳密に考えているときです。
長い答え
その概念は、オブジェクトの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
はそれほど重要ではなく、a
とb
だけが重要です。場合によっては、c
はインスタンス間で異なる可能性がありますが、比較の際には意味がありません。
この問題は、メンバー自身が型のインスタンスでもあり、これらのメンバーすべてが同等性を判断する手段を持つ必要がある場合にも当てはまります。
さらに複雑なのは、JavaScriptではデータとメソッドの違いがぼやけていることです。
オブジェクトは、イベントハンドラとして呼び出されることになっているメソッドを参照することができ、これはその 'value state'の一部とは考えられないでしょう。別のオブジェクトには、重要な計算を実行する関数が割り当てられている可能性がありますが、それによって、このインスタンスが別の関数を参照するという理由だけで他のインスタンスとは異なります。
既存のプロトタイプメソッドの1つが別の関数によってオーバーライドされているオブジェクトについてはどうですか。それは他の点では他の点では同等であるとまだ考えられますか?その質問は、各タイプの特定のケースでのみ回答できます。
前述のように、例外は厳密に型なしのオブジェクトです。その場合、唯一の賢明な選択は、各メンバーの反復的で再帰的な比較です。それでも、関数の「価値」とは何かを尋ねなければなりませんか。
なぜ車輪を作り直すのですか? Lodash を試してみてください。これには、 isEqual() などの必須機能がいくつかあります。
_.isEqual(object, other);
このページの他の例と同じように、 ECMAScript 5 およびブラウザで利用可能な場合はネイティブの最適化を使用して、各キーの値を強制的にチェックします。
注:以前はこの回答で Underscore.js が推奨されていましたが、 lodash はバグの修正と一貫性のある問題への対処というより優れた仕事をしました。
JavaScript for Objectsのデフォルトの等価演算子は、メモリ内の同じ場所を参照するときにtrueになります。
var x = {};
var y = {};
var z = x;
x === y; // => false
x === z; // => true
別の等価演算子が必要な場合は、equals(other)
メソッド、またはそのようなものをクラスに追加する必要があります。問題のドメインの詳細によって、それが何を意味するのかが決まります。
これがトランプカードの例です。
function Card(rank, suit) {
this.rank = rank;
this.suit = suit;
this.equals = function(other) {
return other.rank == this.rank && other.suit == this.suit;
};
}
var queenOfClubs = new Card(12, "C");
var kingOfSpades = new Card(13, "S");
queenOfClubs.equals(kingOfSpades); // => false
kingOfSpades.equals(new Card(13, "S")); // => true
AngularJS で作業している場合、angular.equals
関数は2つのオブジェクトが等しいかどうかを判断します。 Ember.js にisEqual
を使用します。
angular.equals
- この方法についての詳細は docs または source を参照してください。配列についても詳細比較を行います。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>
これは私のバージョンです。 ES5で導入された新しい Object.keys 機能と、 + 、 + 、 + からのアイデア/テストを使用しています。
function objectEquals(x, y) {
'use strict';
if (x === null || x === undefined || y === null || y === undefined) { return x === y; }
// after this just checking type of one would be enough
if (x.constructor !== y.constructor) { return false; }
// if they are functions, they should exactly refer to same one (because of closures)
if (x instanceof Function) { return x === y; }
// if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES)
if (x instanceof RegExp) { return x === y; }
if (x === y || x.valueOf() === y.valueOf()) { return true; }
if (Array.isArray(x) && x.length !== y.length) { return false; }
// if they are dates, they must had equal valueOf
if (x instanceof Date) { return false; }
// if they are strictly equal, they both need to be object at least
if (!(x instanceof Object)) { return false; }
if (!(y instanceof Object)) { return false; }
// recursive object equality check
var p = Object.keys(x);
return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) &&
p.every(function (i) { return objectEquals(x[i], y[i]); });
}
///////////////////////////////////////////////////////////////
/// The borrowed tests, run them by clicking "Run code snippet"
///////////////////////////////////////////////////////////////
var printResult = function (x) {
if (x) { document.write('<div style="color: green;">Passed</div>'); }
else { document.write('<div style="color: red;">Failed</div>'); }
};
var assert = { isTrue: function (x) { printResult(x); }, isFalse: function (x) { printResult(!x); } }
assert.isTrue(objectEquals(null,null));
assert.isFalse(objectEquals(null,undefined));
assert.isFalse(objectEquals(/abc/, /abc/));
assert.isFalse(objectEquals(/abc/, /123/));
var r = /abc/;
assert.isTrue(objectEquals(r, r));
assert.isTrue(objectEquals("hi","hi"));
assert.isTrue(objectEquals(5,5));
assert.isFalse(objectEquals(5,10));
assert.isTrue(objectEquals([],[]));
assert.isTrue(objectEquals([1,2],[1,2]));
assert.isFalse(objectEquals([1,2],[2,1]));
assert.isFalse(objectEquals([1,2],[1,2,3]));
assert.isTrue(objectEquals({},{}));
assert.isTrue(objectEquals({a:1,b:2},{a:1,b:2}));
assert.isTrue(objectEquals({a:1,b:2},{b:2,a:1}));
assert.isFalse(objectEquals({a:1,b:2},{a:1,b:3}));
assert.isTrue(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assert.isFalse(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));
Object.prototype.equals = function (obj) { return objectEquals(this, obj); };
var assertFalse = assert.isFalse,
assertTrue = assert.isTrue;
assertFalse({}.equals(null));
assertFalse({}.equals(undefined));
assertTrue("hi".equals("hi"));
assertTrue(new Number(5).equals(5));
assertFalse(new Number(5).equals(10));
assertFalse(new Number(1).equals("1"));
assertTrue([].equals([]));
assertTrue([1,2].equals([1,2]));
assertFalse([1,2].equals([2,1]));
assertFalse([1,2].equals([1,2,3]));
assertTrue(new Date("2011-03-31").equals(new Date("2011-03-31")));
assertFalse(new Date("2011-03-31").equals(new Date("1970-01-01")));
assertTrue({}.equals({}));
assertTrue({a:1,b:2}.equals({a:1,b:2}));
assertTrue({a:1,b:2}.equals({b:2,a:1}));
assertFalse({a:1,b:2}.equals({a:1,b:3}));
assertTrue({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assertFalse({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));
var a = {a: 'text', b:[0,1]};
var b = {a: 'text', b:[0,1]};
var c = {a: 'text', b: 0};
var d = {a: 'text', b: false};
var e = {a: 'text', b:[1,0]};
var i = {
a: 'text',
c: {
b: [1, 0]
}
};
var j = {
a: 'text',
c: {
b: [1, 0]
}
};
var k = {a: 'text', b: null};
var l = {a: 'text', b: undefined};
assertTrue(a.equals(b));
assertFalse(a.equals(c));
assertFalse(c.equals(d));
assertFalse(a.equals(e));
assertTrue(i.equals(j));
assertFalse(d.equals(k));
assertFalse(k.equals(l));
// from comments on stackoverflow post
assert.isFalse(objectEquals([1, 2, undefined], [1, 2]));
assert.isFalse(objectEquals([1, 2, 3], { 0: 1, 1: 2, 2: 3 }));
assert.isFalse(objectEquals(new Date(1234), 1234));
// no two different function is equal really, they capture their context variables
// so even if they have same toString(), they won't have same functionality
var func = function (x) { return true; };
var func2 = function (x) { return true; };
assert.isTrue(objectEquals(func, func));
assert.isFalse(objectEquals(func, func2));
assert.isTrue(objectEquals({ a: { b: func } }, { a: { b: func } }));
assert.isFalse(objectEquals({ a: { b: func } }, { a: { b: func2 } }));
JSONライブラリを使用している場合は、各オブジェクトをJSONとしてエンコードし、結果の文字列が等しいかどうかを比較できます。
var obj1={test:"value"};
var obj2={test:"value2"};
alert(JSON.encode(obj1)===JSON.encode(obj2));
注:この回答は多くの場合有効ですが、コメントで指摘しているように、さまざまな理由から問題があります。ほとんどすべての場合において、より堅牢なソリューションを見つけたいでしょう。
短い機能的なdeepEqual
の実装:
function deepEqual(x, y) {
return (x && y && typeof x === 'object' && typeof y === 'object') ?
(Object.keys(x).length === Object.keys(y).length) &&
Object.keys(x).reduce(function(isEqual, key) {
return isEqual && deepEqual(x[key], y[key]);
}, true) : (x === y);
}
編集 :バージョン2、ジブの提案とES6の矢印機能を使って:
function deepEqual(x, y) {
const ok = Object.keys, tx = typeof x, ty = typeof y;
return x && y && tx === 'object' && tx === ty ? (
ok(x).length === ok(y).length &&
ok(x).every(key => deepEqual(x[key], y[key]))
) : (x === y);
}
ディープコピー機能が便利な場合は、 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
にすでに存在するプロパティは単純に上書きされるため、クローン内のそれらの順序は保持されます。
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()
関数を使用する方法があります。
すべてを比較するための最も単純な および logical の解決策 Object、Array、String、Int ...
JSON.stringify({a: val1}) === JSON.stringify({a: val2})
注意:
val1
とval2
を置き換える必要がありますNode.jsでは、ネイティブのrequire("assert").deepEqual
を使用できます。より多くの情報: http://nodejs.org/api/assert.html
例えば:
var assert = require("assert");
assert.deepEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError
エラーを返す代わりにtrue
/false
を返す別の例:
var assert = require("assert");
function deepEqual(a, b) {
try {
assert.deepEqual(a, b);
} catch (error) {
if (error.name === "AssertionError") {
return false;
}
throw error;
}
return true;
};
私はこのcomparable
関数を使用して、JSONに匹敵する私のオブジェクトのコピーを作成します。
var comparable = o => (typeof o != 'object' || !o)? o :
Object.keys(o).sort().reduce((c, key) => (c[key] = comparable(o[key]), c), {});
// Demo:
var a = { a: 1, c: 4, b: [2, 3], d: { e: '5', f: null } };
var b = { b: [2, 3], c: 4, d: { f: null, e: '5' }, a: 1 };
console.log(JSON.stringify(comparable(a)));
console.log(JSON.stringify(comparable(b)));
console.log(JSON.stringify(comparable(a)) == JSON.stringify(comparable(b)));
<div id="div"></div>
テストに便利です(ほとんどのテストフレームワークはis
関数を持っています)。例えば。
is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), 'x must match y');
違いが見つかると、文字列がログに記録され、違いが出てくる可能性があります。
x must match y
got {"a":1,"b":{"0":2,"1":3},"c":7,"d":{"e":"5","f":null}},
expected {"a":1,"b":{"0":2,"1":3},"c":4,"d":{"e":"5","f":null}}.
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;
}
}
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;
}
また、それは再帰的なので、それがあなたがそれをあなたが呼んでいるものであるならば、それはまた深い均等性をチェックすることができます。
多くの人が気付いていないこの問題に対する簡単な解決策は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;
});
Babelなどで ES6 + を使用している場合は、Object.is(x, y)
も使用できます。
参照: http://wiki.ecmascript.org/doku.php?id=harmony:egal#object.is_x_y
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:{...}}
ライクオブジェクトが返されます。
私は(JSONの解決策が示唆するように)ハッシュやシリアライゼーションに対して忠告したいと思います。 2つのオブジェクトが等しいかどうかをテストする必要がある場合は、等しいという意味を定義する必要があります。両方のオブジェクトのすべてのデータメンバが一致しているか、メモリの場所が一致している(両方の変数がメモリ内の同じオブジェクトを参照している)か、各オブジェクトの1つのデータメンバだけが一致している必要があります。
私は最近、インスタンスが作成されるたびにコンストラクタが新しいID(1から始まり1ずつ増加する)を作成するオブジェクトを開発しました。このオブジェクトには、そのid値を別のオブジェクトのid値と比較し、一致する場合にtrueを返すisEqual関数があります。
その場合、id値が一致することを意味するものとして「等しい」と定義しました。各インスタンスが一意のIDを持つことを考えると、これは、一致するオブジェクトも同じメモリ位置を占有するという考えを強制するために使用できます。それは必要ありませんが。
掲載されていたものより一般的なオブジェクト比較関数が必要なので、私は以下を作り上げました。批評を感謝します...
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;
}
私は同じ問題に直面し、私自身の解決策を書くことにしました。しかし、私はまた、配列とオブジェクトを比較したい、そしてその逆もまた同様にしたいので、私は一般的な解決策を作りました。プロトタイプに関数を追加することにしましたが、それらをスタンドアロン関数に簡単に書き換えることができます。これがコードです:
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と同じではありません。
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;
}
)
}
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つのオブジェクトをそれらのキー名でキー順に並べ替えます。
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)
これは上記すべての追加であり、代替ではありません。余分な再帰的なケースをチェックする必要なしに、オブジェクトを高速に浅比較する必要がある場合。これはショットです。
これは、次のものを比較します。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;
}
これは、入力が少なく、簡単な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 { ... }
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
この関数では、次のように仮定しています。
これは単純な戦略の実証として扱われるべきです。
/**
* 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;
}
私はこれが少し古いことを知っています、しかし私はこの問題のために思い付いた解決策を加えたいと思います。オブジェクトがあり、そのデータがいつ変更されたのかを知りたかったのです。 「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を返すからです。
単純なキーと値のペアのオブジェクトインスタンスのキーを比較するには、次のようにします。
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以上のプロパティを持つことができないことを願っています...
私の個人的な図書館から引き出す、私は私の仕事のために繰り返し使う。次の関数は緩やかな再帰的な深等価で、 はチェックしません
私は主にこれを使って、さまざまな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つのインスタンスがメモリ内の同じ場所を指している場合、それらが「等しい」と見なされるケースが1つありますが、それは必ずしも望んでいることとは限りません。たとえば、Personクラスがある場合、2つのPersonオブジェクトの姓、名、および社会保障番号が同じであれば、それらは「等しい」と見なすことができます(それらがメモリ内の異なる場所を指している場合でも)。
その一方で、2つのオブジェクトがそれぞれのメンバの値が同じであれば、それらが等しいとは単純に言えません。つまり、クラスごとに、どのメンバーがオブジェクトの「アイデンティティー」を構成するのかを定義し、適切な等価演算子(==演算子やEqualsメソッドのオーバーロードを介して)を開発するのはクラス開発者の責任です。
2つのオブジェクトが同じハッシュを持つ場合、それらが等しいと言うのは一方通行です。しかし、インスタンスごとにハッシュがどのように計算されるのか疑問に思う必要があります。上記のPersonの例に戻り、ハッシュがFirst Name、Last Name、およびSocial Security Numberの各フィールドの値を調べて計算された場合は、このシステムを使用できます。それに加えて、私たちはそれからハッシュ方法の品質に頼っています(それはそれ自体が大きなトピックですが、すべてのハッシュが等しく作成されるとは限らず、悪いハッシュ方法はmoreにつながる)この場合、誤った一致が返されます。
この問題に対する非常に簡単な修正方法があります。2つを比較すると、両方のオブジェクトに対してJSON.stringify()を実行するだけです。
オブジェクト内のプロパティの順序は変更されないと仮定します。
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));
による。オブジェクト内のキーの順序が重要ではない場合、そして私はそのオブジェクトのプロトタイプを知る必要はありません。使うことはいつも仕事をする。
const object = {};
JSON.stringify(object) === "{}" will pass but {} === "{}" will not
私はJavascriptの専門家ではありませんが、これを解決するための簡単な試みの1つです。私は3つのことを確認します。
object
であり、typeof null
はnull
であるためobject
でもありません。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));
これは、オブジェクトの「値の等価性」をチェックするための非常に基本的な方法です。
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));
ご覧のとおり、オブジェクトの「値の等価性」を確認するには、オブジェクト内のすべてのプロパティについて、それらが等しいかどうかを確認するために反復する必要があります。そして、この単純な実装は私たちの例のために働きますが、それが扱わない多くの場合があります。例えば:
オブジェクトの「値の同等性」を確認するための堅牢な方法としては、 Underscore のようなさまざまなEdgeのケースをカバーする、十分にテストされたライブラリに頼るのが良いでしょう。
var john = {
occupation: "Web Developer",
age: 25
};
var bobby = {
occupation: "Web Developer",
age: 25
};
// Outputs: true
console.log(_.isEqual(john, bobby));
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'}))
私はNode.jsとcompare.jsというブラウザで動作する小さなライブラリを書きました。 ==、!=、>、> =、<、<=などの通常の比較演算子と、JavaScriptのすべてのデータ型でのIDを提供します。
例えば、あなたは使用することができます
cmp.eq(obj1, obj2);
そしてこれは平等かどうかをチェックするでしょう(深い等しいアプローチを使って)。それ以外の場合は、
cmp.id(obj1, obj2);
それは参照によって比較されます、それ故にアイデンティティをチェックします。オブジェクトに<と>を使用することもできます。これはサブセットとスーパーセットを意味します。
compare.jsは約700の単体テストでカバーされているので、うまくいけばバグが多すぎないはずです;-)。
あなたはそれを見つけることができます https://github.com/goloroden/compare.js 無料で、それはMITライセンスの下でオープンソースです。
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.
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));
オブジェクト等価性検査:JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())
上記のテストは、オブジェクトの配列に対しても機能します。その場合、 http://www.w3schools.com/jsref/jsref_sort.asp に記載されているようにソート関数を使用します。
フラットなJSONスキーマを持つ小さな配列には十分かもしれません。
2つのオブジェクトが似ているかどうかを確認するための簡単な「ハック」は、それらのtoString()メソッドを使用することです。オブジェクトAとBをチェックしている場合は、AとBに意味のあるtoString()メソッドがあることを確認し、それらが返す文字列が同じであることを確認します。
これは万能薬ではありませんが、正しい状況では役に立つことがあります。
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' });
これは受け入れられますか?
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;
};
私は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>
ええ、別の答え...
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>
私はすべてのサブオブジェクトまたは配列に深く入るであろうはるかに短い関数を持っています。これは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
Function
、Date
、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();
}
確かに、私たちがそれにたどり着いている間、私は私自身の車輪の再発明を投げ入れるでしょう(私はスポークと使用される材料の数を誇りに思います):
////////////////////////////////////////////////////////////////////////////////
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を返しますが、もちろん、違いが深くネストされている大きなオブジェクトの場合は、それほど効果的ではありません。私自身のシナリオでは、入れ子になった配列をうまく処理することが重要です。
このような「ホイール」を必要としている人に役立つことを願っています。
以下の解決策のうちのいくつかはパフォーマンス、機能性およびスタイルに問題があります...それらは十分に考え抜かれていません、そしてそれらのうちのいくつかはさまざまなケースで失敗します。私は自分自身の解決策でこの問題に対処しようとしました、そしてあなたのフィードバックに本当に感謝します。
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;
};
これは、入力として要素の配列を受け取り、それらを互いに比較する一般的な等価検査関数です。あらゆる種類の要素で動作します。
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);
});
これはとてもきれいな 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"})
たくさんの良い考えがここにあります!これがディープイコールの私のバージョンです。私はそれをgithubに投稿し、その周りにいくつかのテストを書きました。考えられるすべてのケースを網羅するのは困難であり、時にはそれが不要な場合もあります。
私はNaN !== NaN
と循環依存をカバーしました。
https://github.com/ryancat/simple-deep-equal/blob/master/index.js
私のバージョンには、違いが見つかった場所と、違いが何であるかのチェーンが含まれています。
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');
}
}
これがこの問題に対する私の解決策です。私は素晴らしいとは思わないが、それはあらゆるタイプとのオブジェクト比較で機能する
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;
};