如何确定平等的两JavaScript的对象?
-
03-07-2019 - |
题
一个严格的平等,操作员会告诉你的如果两个物体 类型 都是平等的。然而,有没有办法告诉我们,如果两个对象都是平等的, 很像列码 值在Java?
堆溢出问题 是否有任何种类的哈希码功能在JavaScript? 类似于这个问题,但需要更多的学术答案。上述情况表明为什么就必须有一个,我想知道如果有任何 相当的解决方案.
解决方案
简短回答
简单的答案是:不,没有通用的方法来确定一个对象在你的意思上是否与另一个对象相等。例外情况是您严格考虑对象是无类型的。
答案很长
概念是Equals方法,它比较对象的两个不同实例,以指示它们在值级别上是否相等。但是,由特定类型决定如何实现 Equals
方法。具有原始值的属性的迭代比较可能是不够的,可能存在不被视为对象值的一部分的属性。例如,
function MyClass(a, b)
{
var c;
this.getCLazy = function() {
if (c === undefined) c = a * b // imagine * is really expensive
return c;
}
}
在上面的例子中, c
对于确定MyClass的任何两个实例是否相等并不重要,只有 a
和 b
是重要。在某些情况下, c
可能在实例之间有所不同,但在比较期间并不重要。
请注意,当成员本身也可以是某种类型的实例时,这个问题也适用,并且这些实例都需要具备确定相等性的方法。
更复杂的是,在JavaScript中,数据和方法之间的区别很模糊。
对象可以引用一个被称为事件处理程序的方法,这可能不会被视为其“值状态”的一部分。而另一个对象可能会被分配一个执行重要计算的函数,从而使该实例与其他对象不同,因为它引用了不同的函数。
如果某个对象的某个现有原型方法被另一个函数覆盖了?是否仍然可以认为它与其他实例相同?这个问题只能在每种类型的每个特定情况下得到解答。
如前所述,异常将是严格无类型的对象。在这种情况下,唯一明智的选择是每个成员的迭代和递归比较。即使这样,人们也要问一个函数的“价值”是什么?
其他提示
为什么重新发明轮子?尝试 Lodash 。它有许多必备功能,例如 isEqual()。
_.isEqual(object, other);
将使用 ECMAScript&nbsp来强制检查每个键值 - 就像本页上的其他示例一样; 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
如果你是在工作 人们的生活产生了重要, , angular.equals
功能将确定两种对象都是平等的。在 Ember.js 使用 isEqual
.
angular.equals
-看到 文档 或 来源 欲了解更多有关这一点的方法。它并深比较上阵列。- Ember.js
isEqual
-看到 文档 或 来源 欲了解更多有关这一点的方法。它不会做一个深比较上阵列。
var purple = [{"purple": "drank"}];
var drank = [{"purple": "drank"}];
if(angular.equals(purple, drank)) {
document.write('got dat');
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
这是我的版本。它正在使用新的 Object.keys 功能在ES5和想法/测试中引入了 + , + 和 + :
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,使用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);
}
如果你有一个深度复制功能,你可以使用以下技巧仍然使用 JSON.stringify
,同时匹配属性的顺序:
function equals(obj1, obj2) {
function _equals(obj1, obj2) {
return JSON.stringify(obj1)
=== JSON.stringify($.extend(true, {}, obj1, obj2));
}
return _equals(obj1, obj2) && _equals(obj2, obj1);
}
演示: http://jsfiddle.net/CU3vb/3/
理由:
由于 obj1
的属性逐个复制到克隆,因此将保留它们在克隆中的顺序。当 obj2
的属性被复制到克隆时,由于 obj1
中已存在的属性将被覆盖,因此将保留克隆中的顺序。
你是否试图测试两个物体是否相等?即:他们的财产是平等的?
如果是这种情况,您可能已经注意到了这种情况:
var a = { foo : "bar" };
var b = { foo : "bar" };
alert (a == b ? "Equal" : "Not equal");
// "Not equal"
你可能需要这样做:
function objectEquals(obj1, obj2) {
for (var i in obj1) {
if (obj1.hasOwnProperty(i)) {
if (!obj2.hasOwnProperty(i)) return false;
if (obj1[i] != obj2[i]) return false;
}
}
for (var i in obj2) {
if (obj2.hasOwnProperty(i)) {
if (!obj1.hasOwnProperty(i)) return false;
if (obj1[i] != obj2[i]) return false;
}
}
return true;
}
显然,该函数可以进行相当多的优化,并且可以进行深度检查(处理嵌套对象: var a = {foo:{fu:&quot; bar&quot;}}
)但你明白了。
正如FOR指出的那样,您可能必须根据自己的目的对其进行调整,例如:不同的类可能有不同的“相等”定义。如果您只是使用普通对象,上面的内容就足够了,否则自定义 MyClass.equals()
函数可能就好了。
最简单的 和 逻辑 解决方案进行比较的一切都喜欢 目,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;
};
我使用这个可比较的
函数来生成与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>
在测试中派上用场(大多数测试框架都有是
函数)。 E.g。
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;
}
}
我不知道是否有人发布了与此相似的内容,但这是我为检查对象平等而做的功能。
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;
}
此外,它是递归的,所以它也可以检查深度相等,如果这就是你所谓的。
您可以使用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
许多人没有意识到的这个问题的简单解决方案是对JSON字符串进行排序(每个字符)。这通常也比这里提到的其他解决方案更快:
function areEqual(obj1, obj2) {
var a = JSON.stringify(obj1), b = JSON.stringify(obj2);
if (!a) a = '';
if (!b) b = '';
return (a.split('').sort().join('') == b.split('').sort().join(''));
}
此方法的另一个有用之处是您可以通过传递“replacer”来过滤比较。功能到JSON.stringify函数( https:// developer。 mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_of_using_replacer_parameter )。以下内容仅比较名为“derp”的所有对象键:
function areEqual(obj1, obj2, filter) {
var a = JSON.stringify(obj1, filter), b = JSON.stringify(obj2, filter);
if (!a) a = '';
if (!b) b = '';
return (a.split('').sort().join('') == b.split('').sort().join(''));
}
var equal = areEqual(obj1, obj2, function(key, value) {
return (key === 'derp') ? value : undefined;
});
我建议不要使用散列或序列化(如JSON解决方案所示)。如果您需要测试两个对象是否相等,那么您需要定义equals的含义。可能是两个对象中的所有数据成员都匹配,或者可能是内存位置必须匹配(意味着两个变量都引用内存中的同一个对象),或者每个对象中只有一个数据成员必须匹配。
最近,我开发了一个对象,每次创建实例时,构造函数都会创建一个新的id(从1开始并递增1)。该对象有一个isEqual函数,用于将该id值与另一个对象的id值进行比较,如果匹配则返回true。
在那种情况下,我定义了“相等”。表示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;
}
如果您要比较JSON对象,可以使用 https://github.com/mirek/node -rus-DIFF
npm install rus-diff
用法:
a = {foo:{bar:1}}
b = {foo:{bar:1}}
c = {foo:{bar:2}}
var rusDiff = require('rus-diff').rusDiff
console.log(rusDiff(a, b)) // -> false, meaning a and b are equal
console.log(rusDiff(a, c)) // -> { '$set': { 'foo.bar': 2 } }
如果两个对象不同,那么兼容MongoDB的 {$ rename:{...},$ unset:{...},$ set:{...}}
就像对象一样返回。
我遇到了同样的问题,并决定编写自己的解决方案。但是因为我想比较Arrays和Objects,反之亦然,我制作了一个通用的解决方案。我决定将这些函数添加到原型中,但可以轻松地将它们重写为独立函数。这是代码:
Array.prototype.equals = Object.prototype.equals = function(b) {
var ar = JSON.parse(JSON.stringify(b));
var err = false;
for(var key in this) {
if(this.hasOwnProperty(key)) {
var found = ar.find(this[key]);
if(found > -1) {
if(Object.prototype.toString.call(ar) === "[object Object]") {
delete ar[Object.keys(ar)[found]];
}
else {
ar.splice(found, 1);
}
}
else {
err = true;
break;
}
}
};
if(Object.keys(ar).length > 0 || err) {
return false;
}
return true;
}
Array.prototype.find = Object.prototype.find = function(v) {
var f = -1;
for(var i in this) {
if(this.hasOwnProperty(i)) {
if(Object.prototype.toString.call(this[i]) === "[object Array]" || Object.prototype.toString.call(this[i]) === "[object Object]") {
if(this[i].equals(v)) {
f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
}
}
else if(this[i] === v) {
f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
}
}
}
return f;
}
该算法分为两部分; equals函数本身和一个函数,用于查找数组/对象中属性的数字索引。只需要查找函数,因为indexof只查找数字和字符串而没有对象。
可以这样称呼它:
({a: 1, b: "h"}).equals({a: 1, b: "h"});
该函数返回true或false,在本例中为true。 算法允许比较非常复杂的对象:
({a: 1, b: "hello", c: ["w", "o", "r", "l", "d", {answer1: "should be", answer2: true}]}).equals({b: "hello", a: 1, c: ["w", "d", "o", "r", {answer1: "should be", answer2: true}, "l"]})
上面的示例将返回true,即使属性具有不同的顺序。需要注意的一个小细节:此代码还检查相同类型的两个变量,因此“3”表示“3”。与3不一样。
只是想利用一些es6功能贡献我的对象版本比较。它没有考虑订单。将所有if / else转换为三元后,我得到了以下内容:
function areEqual(obj1, obj2) {
return Object.keys(obj1).every(key => {
return obj2.hasOwnProperty(key) ?
typeof obj1[key] === 'object' ?
areEqual(obj1[key], obj2[key]) :
obj1[key] === obj2[key] :
false;
}
)
}
如果两个对象的所有属性具有相同的值,并且对所有嵌套对象和数组都是递归的,则认为这两个对象是相同的。我还认为以下两个对象是相同的:
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;
}
我正在使用此函数做出以下假设:
- 您可以控制要比较的对象,并且只有原始值(即不是嵌套对象,函数等)。
- 您的浏览器支持 Object.keys 。 醇>
这应被视为一种简单策略的证明。
/**
* Checks the equality of two objects that contain primitive values. (ie. no nested objects, functions, etc.)
* @param {Object} object1
* @param {Object} object2
* @param {Boolean} [order_matters] Affects the return value of unordered objects. (ex. {a:1, b:2} and {b:2, a:1}).
* @returns {Boolean}
*/
function isEqual( object1, object2, order_matters ) {
var keys1 = Object.keys(object1),
keys2 = Object.keys(object2),
i, key;
// Test 1: Same number of elements
if( keys1.length != keys2.length ) {
return false;
}
// If order doesn't matter isEqual({a:2, b:1}, {b:1, a:2}) should return true.
// keys1 = Object.keys({a:2, b:1}) = ["a","b"];
// keys2 = Object.keys({b:1, a:2}) = ["b","a"];
// This is why we are sorting keys1 and keys2.
if( !order_matters ) {
keys1.sort();
keys2.sort();
}
// Test 2: Same keys
for( i = 0; i < keys1.length; i++ ) {
if( keys1[i] != keys2[i] ) {
return false;
}
}
// Test 3: Values
for( i = 0; i < keys1.length; i++ ) {
key = keys1[i];
if( object1[key] != object2[key] ) {
return false;
}
}
return true;
}
这是上述所有内容的补充,而不是替代品。如果你需要快速浅比较对象而不需要检查额外的递归情况。这是一个镜头。
比较:1)自有属性的数量相等,2)键名相等,3)如果bCompareValues == true,相应属性值的等价及其类型(三重相等)
var shallowCompareObjects = function(o1, o2, bCompareValues) {
var s,
n1 = 0,
n2 = 0,
b = true;
for (s in o1) { n1 ++; }
for (s in o2) {
if (!o1.hasOwnProperty(s)) {
b = false;
break;
}
if (bCompareValues && o1[s] !== o2[s]) {
b = false;
break;
}
n2 ++;
}
return b && n1 == n2;
}
用于比较键为简单的关键值对象的情况下,我使用:
function compareKeys(r1, r2) {
var nloops = 0, score = 0;
for(k1 in r1) {
for(k2 in r2) {
nloops++;
if(k1 == k2)
score++;
}
}
return nloops == (score * score);
};
一旦键比较,一个简单的额外 for..in
循环是不够的。
复杂性是O(N*N)与N的数键。
我希望/猜对象我一定不会保持超过1000个性...
我知道这有点旧,但我想添加一个解决方案,我想出了这个问题。 我有一个对象,我想知道它的数据何时发生变化。 “与Object.observe类似的东西”我做的是:
function checkObjects(obj,obj2){
var values = [];
var keys = [];
keys = Object.keys(obj);
keys.forEach(function(key){
values.push(key);
});
var values2 = [];
var keys2 = [];
keys2 = Object.keys(obj2);
keys2.forEach(function(key){
values2.push(key);
});
return (values == values2 && keys == keys2)
}
这里可以复制并创建另一组数组来比较值和键。 它非常简单,因为它们现在是数组,如果对象具有不同的大小,它将返回false。
拉出去从我个人的图书馆,这是我用我的工作重复。下列函数是一种宽松的递归深刻的平等, 不检查
- 类平等
- 继承价值观
- 值严格的平等
我主要是使用这种检查,如果我得到平等的答复对各种API执行情况。在那里执行情况的差异(如串vs号)和其他空值,可能会发生。
它的实现是相当直截了当和简短的(如果所有的意见是剥离)
/** Recursively check if both objects are equal in value
***
*** This function is designed to use multiple methods from most probable
*** (and in most cases) valid, to the more regid and complex method.
***
*** One of the main principles behind the various check is that while
*** some of the simpler checks such as == or JSON may cause false negatives,
*** they do not cause false positives. As such they can be safely run first.
***
*** # !Important Note:
*** as this function is designed for simplified deep equal checks it is not designed
*** for the following
***
*** - Class equality, (ClassA().a = 1) maybe valid to (ClassB().b = 1)
*** - Inherited values, this actually ignores them
*** - Values being strictly equal, "1" is equal to 1 (see the basic equality check on this)
*** - Performance across all cases. This is designed for high performance on the
*** most probable cases of == / JSON equality. Consider bench testing, if you have
*** more 'complex' requirments
***
*** @param objA : First object to compare
*** @param objB : 2nd object to compare
*** @param .... : Any other objects to compare
***
*** @returns true if all equals, or false if invalid
***
*** @license Copyright by eugene@picoded.com, 2012.
*** Licensed under the MIT license: http://opensource.org/licenses/MIT
**/
function simpleRecusiveDeepEqual(objA, objB) {
// Multiple comparision check
//--------------------------------------------
var args = Array.prototype.slice.call(arguments);
if(args.length > 2) {
for(var a=1; a<args.length; ++a) {
if(!simpleRecusiveDeepEqual(args[a-1], args[a])) {
return false;
}
}
return true;
} else if(args.length < 2) {
throw "simpleRecusiveDeepEqual, requires atleast 2 arguments";
}
// basic equality check,
//--------------------------------------------
// if this succed the 2 basic values is equal,
// such as numbers and string.
//
// or its actually the same object pointer. Bam
//
// Note that if string and number strictly equal is required
// change the equality from ==, to ===
//
if(objA == objB) {
return true;
}
// If a value is a bsic type, and failed above. This fails
var basicTypes = ["boolean", "number", "string"];
if( basicTypes.indexOf(typeof objA) >= 0 || basicTypes.indexOf(typeof objB) >= 0 ) {
return false;
}
// JSON equality check,
//--------------------------------------------
// this can fail, if the JSON stringify the objects in the wrong order
// for example the following may fail, due to different string order:
//
// JSON.stringify( {a:1, b:2} ) == JSON.stringify( {b:2, a:1} )
//
if(JSON.stringify(objA) == JSON.stringify(objB)) {
return true;
}
// Array equality check
//--------------------------------------------
// This is performed prior to iteration check,
// Without this check the following would have been considered valid
//
// simpleRecusiveDeepEqual( { 0:1963 }, [1963] );
//
// Note that u may remove this segment if this is what is intended
//
if( Array.isArray(objA) ) {
//objA is array, objB is not an array
if( !Array.isArray(objB) ) {
return false;
}
} else if( Array.isArray(objB) ) {
//objA is not array, objB is an array
return false;
}
// Nested values iteration
//--------------------------------------------
// Scan and iterate all the nested values, and check for non equal values recusively
//
// Note that this does not check against null equality, remove the various "!= null"
// if this is required
var i; //reuse var to iterate
// Check objA values against objB
for (i in objA) {
//Protect against inherited properties
if(objA.hasOwnProperty(i)) {
if(objB.hasOwnProperty(i)) {
// Check if deep equal is valid
if(!simpleRecusiveDeepEqual( objA[i], objB[i] )) {
return false;
}
} else if(objA[i] != null) {
//ignore null values in objA, that objB does not have
//else fails
return false;
}
}
}
// Check if objB has additional values, that objA do not, fail if so
for (i in objB) {
if(objB.hasOwnProperty(i)) {
if(objB[i] != null && !objA.hasOwnProperty(i)) {
//ignore null values in objB, that objA does not have
//else fails
return false;
}
}
}
// End of all checks
//--------------------------------------------
// By reaching here, all iteration scans have been done.
// and should have returned false if it failed
return true;
}
// Sanity checking of simpleRecusiveDeepEqual
(function() {
if(
// Basic checks
!simpleRecusiveDeepEqual({}, {}) ||
!simpleRecusiveDeepEqual([], []) ||
!simpleRecusiveDeepEqual(['a'], ['a']) ||
// Not strict checks
!simpleRecusiveDeepEqual("1", 1) ||
// Multiple objects check
!simpleRecusiveDeepEqual( { a:[1,2] }, { a:[1,2] }, { a:[1,2] } ) ||
// Ensure distinction between array and object (the following should fail)
simpleRecusiveDeepEqual( [1963], { 0:1963 } ) ||
// Null strict checks
simpleRecusiveDeepEqual( 0, null ) ||
simpleRecusiveDeepEqual( "", null ) ||
// Last "false" exists to make the various check above easy to comment in/out
false
) {
alert("FATAL ERROR: simpleRecusiveDeepEqual failed basic checks");
} else {
//added this last line, for SO snippet alert on success
alert("simpleRecusiveDeepEqual: Passed all checks, Yays!");
}
})();
这是stringify技巧的一个版本,它可以减少输入,并且可以在很多情况下进行简单的JSON数据比较。
var obj1Fingerprint = JSON.stringify(obj1).replace(/\{|\}/g,'').split(',').sort().join(',');
var obj2Fingerprint = JSON.stringify(obj2).replace(/\{|\}/g,'').split(',').sort().join(',');
if ( obj1Fingerprint === obj2Fingerprint) { ... } else { ... }
我看到意大利面条代码答案。 不使用任何第三方库,这很容易。
首先按键键对两个对象进行排序。
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)
对于那些使用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