严格相等运算符会告诉您两个对象类型是否相等。但是,有没有办法判断两个对象是否相等,就像Java 中的哈希码值一样?
堆栈溢出问题JavaScript 中是否有任何类型的 hashCode 函数?与this question类似,但需要更学术的答案。上面的场景说明了为什么有必要拥有一个,我想知道是否有任何等效的解决方案。
严格相等运算符会告诉您两个对象类型是否相等。但是,有没有办法判断两个对象是否相等,就像Java 中的哈希码值一样?
堆栈溢出问题JavaScript 中是否有任何类型的 hashCode 函数?与this question类似,但需要更学术的答案。上面的场景说明了为什么有必要拥有一个,我想知道是否有任何等效的解决方案。
为什么要重新发明轮子?试试Lodash 。它具有许多必备功能,例如isEqual()。
_.isEqual(object, other);
它将使用ECMAScript 5和本机优化(如果它们在浏览器中可用)来强力检查每个键值 - 就像本页上的其他示例一样。
注意:以前这个答案推荐Underscore.js,但lodash在修复错误和解决一致性问题方面做得更好。
简短的回答
简单的答案是:不,没有通用的方法可以确定一个对象与您所说的另一个对象相等。例外情况是当您严格认为对象是无类型的时。
长答案
这个概念是 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 中,数据和方法之间的区别是模糊的。
一个对象可能引用一个作为事件处理程序调用的方法,这可能不会被视为其“值状态”的一部分。而另一个对象很可能被分配一个执行重要计算的函数,从而使这个实例与其他实例不同,因为它引用了不同的函数。
如果一个对象的一个现有原型方法被另一个函数覆盖了呢?它是否仍然可以被认为与另一个相同的实例相同?这个问题只能在每种类型的每个特定情况下回答。
如前所述,异常将是严格无类型的对象。在这种情况下,唯一明智的选择是对每个成员进行迭代和递归比较。即便如此,人们也不得不问函数的“价值”是什么?
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
函数将确定两个对象是否相等。在Ember.js 中使用isEqual
.
angular.equals
- 有关此方法的更多信息,请参阅文档或来源。它也对数组进行了深入比较。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>
这是我的版本。它使用 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,使用 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);
}
在 Node.js 中,您可以使用其 native require("assert").deepStrictEqual
. 更多信息:
http ://nodejs.org/api/assert.html
例如:
var assert = require("assert");
assert.deepStrictEqual({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;
};
对于那些使用 Node 的人,有一个调用isDeepStrictEqual
本机util
库的便捷方法可以实现这一点。
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
您是否尝试测试两个对象是否相等?即:它们的属性是否相等?
如果是这种情况,您可能已经注意到这种情况:
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()
函数可能是要走的路。
如果你有一个方便的深拷贝功能,你可以使用以下技巧在匹配属性顺序时仍然使用: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
将被简单地覆盖,因此它们在克隆中的顺序将被保留。
比较对象、数组、字符串、整数等所有内容的最简单和合乎逻辑的解决方案...
JSON.stringify({a: val1}) === JSON.stringify({a: val2})
笔记:
val1
和val2
var object1 = {name: "humza" , gender : "male", age: 23}
var object2 = {name: "humza" , gender : "male", age: 23}
var result = Object.keys(object1).every((key) => object1[key] === object2[key])
如果 object1 在 object2 上具有相同的值,则结果将为真。
我使用这个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}}.
这个问题已经有30多个答案了。我将总结和解释它们(用“我的父亲”类比)并添加我建议的解决方案。
您有4+1 类解决方案:
如果您赶时间并且 99% 的正确性有效,那就太好了。
这方面的例子是Pratik BhalodiyaJSON.stringify()
或Joel Anair或其他方法将您的对象转换为字符串,然后使用逐个字符比较两个字符串的方法。JSON.encode
.toString()
===
然而,缺点是字符串中的对象没有全局标准的唯一表示。例如{ a: 5, b: 8}
和{b: 8 and a: 5 }
相等。
说起我的爸爸,“我高大帅哥”和“我高大帅哥爸爸”是同一个人!但是这两个字符串不一样。
请注意,英语语法中实际上有一个正确(标准方式)的形容词顺序,它说它应该是一个“英俊的高个子”,但如果你盲目地假设 iOS 8 Safari 的 Javascript 引擎也遵守同样的语法,盲目!#WelcomeToJavascriptNonStandards
很好,如果你正在学习。
示例是atmin 的解决方案。
最大的缺点是你肯定会错过一些边缘情况。您是否考虑过对象值中的自引用?你考虑过NaN
吗?您是否考虑过两个具有相同ownProperties
但不同原型父对象的对象?
我只会鼓励人们在练习并且代码不会投入生产时这样做。这是重新发明轮子的唯一理由。
这就像假设如果我父亲的名字是“John Smith”并且他的生日是“1/1/1970”,那么任何名为“John Smith”并且出生于“1970 年 1 月 1 日”的人都是我的父亲。
通常是这样,但如果那天有两个“约翰·史密斯”出生怎么办?如果您认为您会考虑他们的身高,那么这会提高准确性,但仍然不是一个完美的比较。
与其疯狂地递归检查所有属性,不如考虑只检查“有限”数量的属性。例如,如果对象是User
s,您可以比较它们的emailAddress
字段。
它仍然不是一个完美的解决方案,但解决方案 #2 的好处是:
equal
函数的库版本如果您需要生产级别的质量,并且您无法更改系统的设计,那就太好了。
示例是_.equal
lodash,已经在coolaj86 的答案或托尼·哈维(Tony Harvey)的答案或拉斐尔·泽维尔(Rafael Xavier )的节点中提到的Angular 或Ember 中。
ownProperties
但不同原型父对象的两个对象是否应被视为相同。)最后,您可能会无意中解决一个潜在的设计问题;只是说!这就像付钱找我的亲生父亲,根据他的电话、姓名、地址等。
它会花费更多,而且可能比我自己进行背景调查更准确,但不包括像我父亲是移民/庇护并且他的生日未知的极端情况!
如果您 [仍然] 可以更改系统的设计(您正在处理的对象)并且您希望您的代码能够持续很长时间,那就太好了。
它并非在所有情况下都适用,并且性能可能不是很好。但是,如果你能做到的话,这是一个非常可靠的解决方案。
解决方案是,object
系统中的每个都将具有唯一标识符以及所有其他属性。标识符的唯一性将在生成时得到保证。在比较两个对象时,您将使用此 ID(也称为 UUID/GUID --全局/通用唯一标识符)。即当且仅当这些ID 相等时它们才相等。
ID 可以是简单的数字,也可以是通过库(建议)或一段代码auto_incremental
生成的字符串。您需要做的就是确保它始终是唯一的,如果它可以是内置的,或者在 UUID 的情况下,可以检查所有现有值(例如 MySQL 的列属性)或简单地检查(如果来自库) 依赖于给出极低的碰撞可能性。auto_incremental
UNIQUE
请注意,您还需要始终将 ID 与对象一起存储(以保证其唯一性),并且实时计算它可能不是最好的方法。
就像知道我父亲的社会安全号码是 911-345-9283,所以任何拥有这个 SSN 的人都是我的父亲,任何声称是我父亲的人都必须有这个 SSN。
为了准确性和可靠性,我个人更喜欢解决方案#4(ID)。如果不可能的话,我会选择#2.1 以实现可预测性,然后选择#3。如果两者都不可能,则#2,最后是#1。
这是 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;
}
此外,它是递归的,因此它还可以检查深度相等,如果这就是你所说的。
ES6:我能完成的最少代码是这样的。它通过对表示对象的所有键值数组进行字符串化来递归地进行深度比较,唯一的限制是不比较方法或符号。
const compareObjects = (a, b) => {
let s = (o) => Object.entries(o).sort().map(i => {
if(i[1] instanceof Object) i[1] = s(i[1]);
return i
})
return JSON.stringify(s(a)) === JSON.stringify(s(b))
}
console.log(compareObjects({b:4,a:{b:1}}, {a:{b:1},b:4}));
重要提示:此函数在ARRAY中执行 JSON.stringfy,其中键已排序,而不是在其自身的对象中:
许多人没有意识到这个问题的一个简单解决方案是对 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(''));
}
此方法的另一个有用之处是您可以通过将“替换器”函数传递给 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;
});
只是想利用一些 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;
}
)
}
您可以_.isEqual(obj1, obj2)
从 underscore.js 库中使用。
这是一个例子:
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.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));
需要比已发布的更通用的对象比较功能,我制作了以下内容。批评赞赏...
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 } }
如果两个对象不同,{$rename:{...}, $unset:{...}, $set:{...}}
则返回一个与 MongoDB 兼容的类似对象。
我遇到了同样的问题,并决定编写自己的解决方案。但是因为我还想将数组与对象进行比较,反之亦然,所以我设计了一个通用的解决方案。我决定将这些函数添加到原型中,但可以轻松地将它们重写为独立函数。这是代码:
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 函数本身和一个在数组/对象中查找属性的数字索引的函数。仅需要 find 函数,因为 indexof 仅查找数字和字符串而没有对象。
可以这样称呼它:
({a: 1, b: "h"}).equals({a: 1, b: "h"});
该函数返回真或假,在本例中为真。该算法还允许在非常复杂的对象之间进行比较:
({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 不同。
下面是一个简短的实现,它使用JSON.stringify
但按照@Jor在此处建议的方式对键进行排序。
一些测试来自@EbrahimByagowi here的回答。
当然,通过使用JSON.stringify
,解决方案仅限于 JSON 可序列化类型(字符串、数字、JSON 对象、数组、布尔值、null)。不支持 , 等Date
对象。Function
function objectEquals(obj1, obj2) {
const JSONstringifyOrder = obj => {
const keys = {};
JSON.stringify(obj, (key, value) => {
keys[key] = null;
return value;
});
return JSON.stringify(obj, Object.keys(keys).sort());
};
return JSONstringifyOrder(obj1) === JSONstringifyOrder(obj2);
}
///////////////////////////////////////////////////////////////
/// 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("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}}));
let std1 = {
name: "Abhijeet",
roll: 1
}
let std2 = {
name: "Siddharth",
roll: 2
}
console.log(JSON.stringify(std1) === JSON.stringify(std2))
我建议不要散列或序列化(正如 JSON 解决方案所建议的那样)。如果您需要测试两个对象是否相等,那么您需要定义相等的含义。可能是两个对象中的所有数据成员都匹配,也可能是内存位置必须匹配(意味着两个变量引用内存中的同一个对象),或者可能是每个对象中只有一个数据成员必须匹配。
最近我开发了一个对象,它的构造函数在每次创建实例时都会创建一个新的 id(从 1 开始并递增 1)。该对象有一个 isEqual 函数,该函数将该 id 值与另一个对象的 id 值进行比较,如果它们匹配则返回 true。
在那种情况下,我将“相等”定义为表示 id 值匹配。鉴于每个实例都有一个唯一的 id,这可以用来强化匹配对象也占用相同内存位置的想法。虽然这不是必需的。
如果两个对象的所有属性和递归所有嵌套对象和数组的值都相同,则认为它们相等是很有用的。我还认为以下两个对象相等:
var a = {p1: 1};
var b = {p1: 1, p2: undefined};
同样,数组可能有“缺失”元素和未定义元素。我也会同样对待它们:
var c = [1, 2];
var d = [1, 2, undefined];
实现此相等定义的函数:
function isEqual(a, b) {
if (a === b) {
return true;
}
if (generalType(a) != generalType(b)) {
return false;
}
if (a == b) {
return true;
}
if (typeof a != 'object') {
return false;
}
// null != {}
if (a instanceof Object != b instanceof Object) {
return false;
}
if (a instanceof Date || b instanceof Date) {
if (a instanceof Date != b instanceof Date ||
a.getTime() != b.getTime()) {
return false;
}
}
var allKeys = [].concat(keys(a), keys(b));
uniqueArray(allKeys);
for (var i = 0; i < allKeys.length; i++) {
var prop = allKeys[i];
if (!isEqual(a[prop], b[prop])) {
return false;
}
}
return true;
}
源代码(包括辅助函数、generalType 和 uniqueArray): Unit Test和Test Runner here。
我对这个函数做了以下假设:
这应该被视为一个简单策略的演示。
/**
* 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;
}
为了比较简单键/值对对象实例的键,我使用:
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 个属性...
这是对以上所有内容的补充,而不是替代。如果您需要快速浅比较对象而不需要检查额外的递归情况。这是一个镜头。
这比较: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;
}
我知道这有点老了,但我想添加一个我为这个问题提出的解决方案。我有一个对象,我想知道它的数据何时发生变化。“类似于 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 实现的相同回复。可能出现实现差异(如字符串与数字)和其他空值的地方。
它的实现非常简单和简短(如果所有评论都被剥离)
/** 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!");
}
})();
我看到意大利面条代码答案。不使用任何第三方库,这很容易。
首先通过键名对两个对象进行排序。
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)
如何确定部分对象(Partial<T>)等于打字稿中的原始对象(T)。
function compareTwoObjects<T>(original: T, partial: Partial<T>): boolean {
return !Object.keys(partial).some((key) => partial[key] !== original[key]);
}
PS最初我打算创建一个带有答案的新问题。但是这样的问题已经存在并被标记为重复。
这是使用 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;
}
}
};
我不是 Javascript 专家,但这里有一个简单的尝试来解决它。我检查三件事:
object
,而且不是null
因为typeof 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));
经过这么多搜索,我找到了以下工作解决方案
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; }
更多信息:JavaScript 中的对象平等
我只是为了确保数组和对象都以清晰的方式进行比较而编写了这个方法。
这也应该可以解决问题!:)
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)
}
}
}
}
尽管这个问题已经得到充分回答,但我缺少一种方法:toJSON
界面。
通常你想通过对它们进行字符串化来与对象进行比较,因为这是最快的方法。但是由于属性的顺序,比较经常被认为是错误的。
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
obj1
等等: 和 的字符串表示obj2
被认为是相同的。
提示
如果您无权访问对象的直接生成,则可以简单地附加该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
在对象(没有方法)中,我们需要检查nested Objects
,Arrays
和primitive types
. 对象可以有其他对象和数组(数组也可以包含其他对象和数组),所以我们可以使用递归函数,如下所示:arrayEquals
检查数组是否相等并equals
检查对象是否相等:
function arrayEquals(a, b) {
if (a.length != b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (a[i].constructor !== b[i].constructor) {
return false;
}
if (a[i] instanceof Array && b[i] instanceof Array) {
if (!arrayEquals(a, b)) {
return false;
}
} else if (a[i] instanceof Object && b[i] instanceof Object) {
if (!equals(a[i], b[i])) {
return false;
}
} else if (a[i] !== b[i]) {
return false;
}
}
return true;
}
function equals(a, b) {
for (let el in a) {
if (b.hasOwnProperty(el)) {
if (a[el].constructor !== b[el].constructor) {
return false;
}
if (a[el] instanceof Array && b[el] instanceof Array) {
if (!arrayEquals(a[el], b[el])) {
return false;
}
} else if (a[el] instanceof Object && b[el] instanceof Object) {
if (!equals(a[el], b[el])) {
return false;
}
} else if (a[el] !== b[el]) {
return false;
}
} else {
return false;
}
}
return true;
}
假设你有两个对象:
let a = {
a: 1,
b: { c: 1, d: "test" },
c: 3,
d: [{ a: [1, 2], e: 2 }, "test", { c: 3, q: 5 }],
};
let b = {
a: 1,
b: { c: 1, d: "test" },
c: 3,
d: [{ a: [1, 2], e: 2 }, "test", { c: 3, q: 5 }],
};
在这里使用上述equals
函数,您可以轻松地比较其中两个对象,如下所示:
if(equals(a, b)) {
// do whatever you want
}
在 React 中,您可以使用isEqual
from 'react-fast-compare'。这个答案可能不适用于纯 JavaScript,但在您使用 React 的情况下会很有用。
console.log(isEqual({ hello: 'world' }, { hello: 'world' })) // returns true
React 中最快的深度相等比较。也非常快速的通用深度比较。非常适合 React.memo 和 shouldComponentUpdate。
更多信息可以在这里找到:https ://www.npmjs.com/package/react-fast-compare 。
对两个对象进行字符串化并进行比较
if(JSON.stringify(object1) == JSON.stringify(object2))
{
如果两个对象相同,则为 true
}
else
{
如果不是同一对象,则为 false
}
2022:
我想出了一个轻而易举的简单算法,可以解决大多数边缘情况。
脚步:
如果您保存了扁平对象,则可以重复使用它。
let obj1= {var1:'value1', var2:{ var1:'value1', var2:'value2'}};
let obj2 = {var1:'value1', var2:{ var1:'value11',var2:'value2'}}
let flat1= flattenObject(obj1)
/*
{
'var1':'value1',
'var2.var1':'value1',
'var2.var2':'value2'
}
*/
let flat2= flattenObject(obj2)
/*
{
'var1':'value1',
'var2.var1':'value11',
'var2.var2':'value2'
}
*/
isEqual(flat1, flat2)
/*
false
*/
当然,您可以为这些步骤提供您的实现。但这是我的:
function flattenObject(obj) {
const object = Object.create(null);
const path = [];
const isObject = (value) => Object(value) === value;
function dig(obj) {
for (let [key, value] of Object.entries(obj)) {
path.push(key);
if (isObject(value)) dig(value);
else object[path.join('.')] = value;
path.pop();
}
}
dig(obj);
return object;
}
function isEqual(flat1, flat2) {
for (let key in flat2) {
if (flat1[key] !== flat2[key])
return false
}
// check for missing keys
for (let key in flat1) {
if (!(key in flat2))
return false
}
return true
}
您还可以使用该方法来获取 和 之间的 Diffobj1
对象obj2
。
查看该答案以了解详细信息:Generic deep diff between two objects
取决于你所说的平等是什么意思。因此,作为类的开发者,您可以定义它们的相等性。
有时使用一种情况,如果两个实例指向内存中的同一位置,则它们被认为是“相等的”,但这并不总是您想要的。例如,如果我有一个 Person 类,如果两个 Person 对象具有相同的姓氏、名字和社会保险号(即使它们指向内存中的不同位置),我可能会认为它们是“相等的”。
另一方面,如果两个对象的每个成员的值相同,我们就不能简单地说两个对象相等,因为有时您不希望这样。换句话说,对于每个类,由类开发人员定义构成对象“身份”的成员并开发适当的相等运算符(通过重载 == 运算符或 Equals 方法)。
如果两个对象具有相同的哈希值,则说它们相等是一种出路。但是,您必须想知道如何为每个实例计算哈希值。回到上面的 Person 示例,如果哈希是通过查看 First Name、Last Name 和 Social Security Number 字段的值来计算的,我们可以使用这个系统。最重要的是,我们依赖于散列方法的质量(这本身就是一个很大的话题,但可以说并非所有的散列都是平等的,糟糕的散列方法会导致更多的冲突,这在这种情况会返回错误的匹配)。
判断两个对象是否相似的快速“破解”方法是使用它们的 toString() 方法。如果您正在检查对象 A 和 B,请确保 A 和 B 具有有意义的 toString() 方法并检查它们返回的字符串是否相同。
这不是灵丹妙药,但有时在正确的情况下很有用。
这是检查对象的“值相等”的一种非常基本的方法。
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。
var john = {
occupation: "Web Developer",
age: 25
};
var bobby = {
occupation: "Web Developer",
age: 25
};
// Outputs: true
console.log(_.isEqual(john, bobby));
我的版本,其中包括发现差异的链条,以及差异是什么。
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');
}
}
我已经实现了一个方法,它需要两个 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>
这是 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 { ... }
这里有很多好的想法!这是我的深度平等版本。我将它发布在 github 上并围绕它编写了一些测试。很难涵盖所有可能的情况,有时也没有必要这样做。
我涵盖NaN !== NaN
了循环依赖。
https://github.com/ryancat/simple-deep-equal/blob/master/index.js
取决于。如果对象中键的顺序不重要,我不需要知道所述对象的原型。使用总是做这项工作。
const object = {};
JSON.stringify(object) === "{}" will pass but {} === "{}" will not
这是一个简单的 Javascript 函数,用于比较具有简单键值对的两个对象。该函数将返回一个字符串数组,其中每个字符串是两个对象之间不等式的路径。
function compare(a,b) {
var paths = [];
[...new Set(Object.keys(a).concat(Object.keys(b)))].forEach(key=>{
if(typeof a[key] === 'object' && typeof b[key] === 'object') {
var results = compare(a[key], b[key]);
if(JSON.stringify(results)!=='[]') {
paths.push(...results.map(result=>key.concat("=>"+result)));
}
}
else if (a[key]!==b[key]) {
paths.push(key);
}
})
return paths;
}
如果你只想比较两个对象而不知道不等式的路径,你可以这样做:
if(JSON.stringify(compare(object1, object2))==='[]') {
// the two objects are equal
} else {
// the two objects are not equal
}
比较 JSON 字符串
function areTwoDictsEqual(dictA, dictB) {
function sortDict(dict) {
var keys = Object.keys(dict);
keys.sort();
var newDict = {};
for (var i=0; i<keys.length; i++) {
var key = keys[i];
var value = dict[key];
newDict[key] = value;
}
return newDict;
}
return JSON.stringify(sortDict(dictA)) == JSON.stringify(sortDict(dictB));
}
另一种选择是使用equals
Ramda库:
const c = {a: 1, b: 2};
const d = {b: 2, a: 1};
R.equals(c, d); //=> true
const obj = {
name: "Carl",
age: 15
}
const obj2 = {
name: "Carl",
age: 15,
}
const compareObj = (objects) => {
const res = objects.map((item) => {
return Object.entries(item).flat().join()
})
return res.every((a) => {
return a === res[0]
})
}
console.log(compareObj([obj,obj2]))
如果您真的想比较并返回两个对象的差异。你可以使用这个包:https ://www.npmjs.com/package/deep-diff
或者只使用这个包使用的代码
https://github.com/flitbit/diff/blob/master/index.js
只是不要将其转换为字符串进行比较。
纯 JS 方法:我的答案是基于生成一个字符串,无论属性顺序是否相同,它都会返回相同的值。设置对象可用于切换大小写和空白是否重要。(为了避免失去焦点,我不包括那些支持功能,或者我猜想应该在任何实用程序集中的 isObject。)
这里也没有展示,但是为了减少字符串比较时间,如果对象很大并且想要加快比较速度,您还可以对字符串进行哈希处理并比较子字符串;这仅适用于非常大的对象(当然,错误相等的可能性很小)。
然后你可以比较 genObjStr(obj1) ?= genObjStr(obj2)
function genObjStr(obj, settings) {
// Generate a string that corresponds to an object guarenteed to be the same str even if
// the object have different ordering. The string would largely be used for comparison purposes
var settings = settings||{};
var doStripWhiteSpace = defTrue(settings.doStripWhiteSpace);
var doSetLowerCase = settings.doSetLowerCase||false;
if(isArray(obj)) {
var vals = [];
for(var i = 0; i < obj.length; ++i) {
vals.push(genObjStr(obj[i], settings));
}
vals = arraySort(vals);
return vals.join(`,`);
} else if(isObject(obj)) {
var keys = Object.keys(obj);
keys = arraySort(keys);
var vals = [];
for(var key of keys) {
var value = obj[key];
value = genObjStr(value, settings);
if(doStripWhiteSpace) {
key = removeWhitespace(key);
var value = removeWhitespace(value);
};
if(doSetLowerCase) {
key = key.toLowerCase();
value = value.toLowerCase();
}
vals.push(value);
}
var str = JSON.stringify({keys: keys, vals: vals});
return str
} else {
if(doStripWhiteSpace) {
obj = removeWhitespace(obj);
};
if(doSetLowerCase) {
obj = obj.toLowerCase();
}
return obj
}
}
var obj1 = {foo: 123, bar: `Test`};
var obj2 = {bar: `Test`, foo: 123};
console.log(genObjStr(obj1) == genObjStr(obj1))
如果您的问题是检查两个对象是否相等,那么此函数可能很有用
function equals(a, b) {
const aKeys = Object.keys(a)
const bKeys = Object.keys(b)
if(aKeys.length != bKeys.length) {
return false
}
for(let i = 0;i < aKeys.length;i++) {
if(aKeys[i] != bKeys[i]) {
return false
}
}
for(let i = 0;i < aKeys.length;i++) {
if(a[aKeys[i]] != b[bKeys[i]]) {
return false
}
}
return true
}
首先我们检查这些对象的键列表的长度是否相同,如果不是,我们返回 false 来检查两个对象是否相等,它们必须具有相同的键(=names)和相同的键值,所以我们获取 objA 和 objB 的所有键,然后我们检查它们是否相等,一旦我们发现两个键不相等,然后我们返回 false,然后当所有键相等时,我们循环遍历其中一个键之一对象,然后我们检查它们是否相等,一旦它们不相等,我们返回 false,在两个循环完成后,这意味着它们相等,我们返回 true 注意:此函数仅适用于没有函数的对象
使用 JSON.stringify() 并不总是可靠的。所以这种方法是您问题IMO的最佳解决方案
首先,不,没有通用的方法来确定一个对象是否相等!
但是有一个概念叫做浅等式比较。有一个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
比较方法,请参阅此处。它来自开源fbjs
Facebook 库。
shallowequal(obj1, obj2)
shallowEqual
obj1
在两个值(即和)之间执行浅层相等比较obj2
以确定它们是否相等。
相等是通过遍历给定的键来执行的obj1
,并且只要任何键的值在和false
之间不严格相等就返回。否则,只要所有键的值严格相等,就返回。obj1
obj2
true
我发现在忽略属性顺序的情况下比较两个 javascript 对象的值的一种简单方法是使用 JSON stringify 替换函数:
const compareReplacer = (key, value) => {
if(typeof value === 'object' && !(value instanceof Array))
return Object.entries(value).sort();
return value;
}
export const compareObjects = (a, b) => JSON.stringify(a, compareReplacer) === JSON.stringify(b, compareReplacer);
这将在每一步对属性进行排序,以便字符串结果对属性顺序保持不变。以前可能有人这样做过,但我只是想我会分享它以防万一:)。
我需要模拟 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' });
我编写了一个在 Node.js 和名为 compare.js 的浏览器上运行的小型库。它为 JavaScript 的所有数据类型提供了常用的比较运算符,例如 ==、!=、>、>=、<、<= 和标识。
例如,您可以使用
cmp.eq(obj1, obj2);
这将检查是否相等(使用深度相等的方法)。否则,如果你这样做
cmp.id(obj1, obj2);
它将通过引用进行比较,因此检查身份。您还可以在对象上使用 < 和 >,这意味着子集和超集。
compare.js 包含近 700 个单元测试,因此希望它不会有太多错误 ;-)。
你可以在https://github.com/goloroden/compare.js免费找到它,它是在 MIT 许可下开源的。
以下一些解决方案在性能、功能和风格方面存在问题......他们没有经过足够的考虑,其中一些在不同的情况下失败了。我试图在自己的解决方案中解决这个问题,非常感谢您的反馈:
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;
};
对象相等检查:JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())
上述测试也适用于对象数组,在这种情况下,使用http://www.w3schools.com/jsref/jsref_sort.asp中记录的排序函数
对于具有扁平 JSON 模式的小型数组可能就足够了。
当然,当我们这样做时,我会投入我自己对车轮的改造(我为使用的辐条和材料的数量感到自豪):
////////////////////////////////////////////////////////////////////////////////
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,但当然对于差异嵌套很深的大型对象,它可能不太有效。在我自己的场景中,很好地处理嵌套数组很重要。
希望它可以帮助需要这种“轮子”的人。
是的,另一个答案...
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>
我有一个更短的函数,它将深入到所有子对象或数组中。如果顺序不同(如here所述) ,它与它一样有效,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();
}
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'}))
这是一个经典的 javascript 问题!我创建了一种方法来检查深层对象相等性,该方法具有能够从比较中选择要忽略的属性的功能。参数是要比较的两个对象,以及一个可选的字符串化属性数组以忽略相对路径。
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
const one={name:'mohit' , age:30};
//const two ={name:'mohit',age:30};
const two ={age:30,name:'mohit'};
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;
}
console.log(isEquivalent(one,two))
虽然这个问题已经有很多答案了。我的尝试只是提供另一种实现方式:
const primitveDataTypes = ['number', 'boolean', 'string', 'undefined'];
const isDateOrRegExp = (value) => value instanceof Date || value instanceof RegExp;
const compare = (first, second) => {
let agg = true;
if(typeof first === typeof second && primitveDataTypes.indexOf(typeof first) !== -1 && first !== second){
agg = false;
}
// adding support for Date and RegExp.
else if(isDateOrRegExp(first) || isDateOrRegExp(second)){
if(first.toString() !== second.toString()){
agg = false;
}
}
else {
if(Array.isArray(first) && Array.isArray(second)){
if(first.length === second.length){
for(let i = 0; i < first.length; i++){
if(typeof first[i] === 'object' && typeof second[i] === 'object'){
agg = compare(first[i], second[i]);
}
else if(first[i] !== second[i]){
agg = false;
}
}
} else {
agg = false;
}
} else {
const firstKeys = Object.keys(first);
const secondKeys = Object.keys(second);
if(firstKeys.length !== secondKeys.length){
agg = false;
}
for(let j = 0 ; j < firstKeys.length; j++){
if(firstKeys[j] !== secondKeys[j]){
agg = false;
}
if(first[firstKeys[j]] && second[secondKeys[j]] && typeof first[firstKeys[j]] === 'object' && typeof second[secondKeys[j]] === 'object'){
agg = compare(first[firstKeys[j]], second[secondKeys[j]]);
}
else if(first[firstKeys[j]] !== second[secondKeys[j]]){
agg = false;
}
}
}
}
return agg;
}
console.log('result', compare({a: 1, b: { c: [4, {d:5}, {e:6}]}, r: null}, {a: 1, b: { c: [4, {d:5}, {e:6}]}, r: 'ffd'})); //returns false.
我之前添加了一个答案,但它并不完美,但这个会检查对象的相等性
function equalObjects(myObj1, myObj2){
let firstScore = 0;
let secondScore = 0;
let index=0;
let proprtiesArray = [];
let valuesArray = [];
let firstLength = 0;
let secondLength = 0;
for (const key in myObj1) {
if (myObj1.hasOwnProperty(key)) {
firstLength += 1;
proprtiesArray.push(key);
valuesArray.push(myObj1[key]);
firstScore +=1;
}
}
for (const key in myObj2) {
if (myObj2.hasOwnProperty(key)) {
secondLength += 1;
if (valuesArray[index] === myObj2[key] && proprtiesArray[index] === key) {
secondScore +=1;
}
//console.log(myObj[key]);
index += 1;
}
}
if (secondScore == firstScore && firstLength === secondLength) {
console.log("true", "equal objects");
return true;
} else {
console.log("false", "not equal objects");
return false;
}
}
equalObjects({'firstName':'Ada','lastName':'Lovelace'},{'firstName':'Ada','lastName':'Lovelace'});
equalObjects({'firstName':'Ada','lastName':'Lovelace'},{'firstName':'Ada','lastName1':'Lovelace'});
equalObjects({'firstName':'Ada','lastName':'Lovelace'},{'firstName':'Ada','lastName':'Lovelace', 'missing': false});
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.
这是通用相等检查器函数,它接收元素数组作为输入并将它们相互比较。适用于所有类型的元素。
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);
});
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));
这是一个非常干净的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"})