严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
这取决于你对平等的定义。因此,作为类的开发人员,要由您来定义它们的相等性。
有时会使用一种情况,如果两个实例指向内存中的相同位置,则认为它们是“相等的”,但这并不总是您想要的。例如,如果我有一个Person类,如果两个Person对象具有相同的Last Name、First Name和Social Security Number(即使它们指向内存中的不同位置),我可能会认为它们是“相等的”。
另一方面,我们不能简单地说两个对象是相等的,如果它们的每个成员的值都相同,因为,有时,你并不想这样。换句话说,对于每个类,由类开发人员定义组成对象“标识”的成员并开发适当的相等操作符(通过重载==操作符或Equals方法)。
Saying that two objects are equal if they have the same hash is one way out. However you then have to wonder how the hash is calculated for each instance. Going back to the Person example above, we could use this system if the hash was calculated by looking at the values of the First Name, Last Name, and Social Security Number fields. On top of that, we are then relying on the quality of the hashing method (that's a huge topic on its own, but suffice it to say that not all hashes are created equal, and bad hashing methods can lead to more collisions, which in this case would return false matches).
你是在测试两个对象是否相等吗?即:它们的属性是相等的?
如果是这样的话,你可能会注意到这种情况:
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所指出的,你可能需要根据自己的目的进行调整,例如:不同的类可能对“equal”有不同的定义。如果您只是处理普通对象,上述方法可能就足够了,否则可以使用自定义MyClass.equals()函数。
如果使用JSON库,可以将每个对象编码为JSON,然后比较结果字符串是否相等。
var obj1={test:"value"};
var obj2={test:"value2"};
alert(JSON.encode(obj1)===JSON.encode(obj2));
注意:虽然这个答案在很多情况下都有效,但由于各种原因,一些人在评论中指出了它的问题。在几乎所有情况下,您都希望找到更健壮的解决方案。
简短的回答
简单的答案是:不,没有一般的方法来确定一个对象等于另一个你所指的意义。例外情况是当您严格地认为一个对象是无类型的。
长话短说
这个概念是一个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中,数据和方法之间的区别是模糊的。
一个对象可以引用一个作为事件处理程序调用的方法,这可能不被认为是其“值状态”的一部分。然而,另一个对象很可能被分配一个执行重要计算的函数,从而使这个实例与其他实例不同,仅仅因为它引用了不同的函数。
如果一个对象的现有原型方法被另一个函数覆盖,该怎么办?它还能被认为与另一个相同的实例相等吗?这个问题只能在每种类型的具体情况下回答。
如前所述,异常将是一个严格的无类型对象。在这种情况下,唯一明智的选择是对每个成员进行迭代和递归比较。即使这样,人们也要问一个函数的“值”是什么?
我建议不要使用散列或序列化(正如JSON解决方案所建议的那样)。如果需要测试两个对象是否相等,则需要定义相等的含义。这可能是两个对象中的所有数据成员都匹配,也可能是内存位置必须匹配(意味着两个变量在内存中引用同一个对象),或者每个对象中只有一个数据成员必须匹配。
最近我开发了一个对象,它的构造函数在每次创建实例时都会创建一个新的id(从1开始,加1)。该对象有一个isEqual函数,用于将该id值与另一个对象的id值进行比较,如果匹配则返回true。
在这种情况下,我定义“相等”的意思是id值匹配。假设每个实例都有一个唯一的id,这可以用来加强匹配对象也占用相同内存位置的想法。尽管这是不必要的。
在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
我需要模拟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' });
需要一个比已经发布的更通用的对象比较函数,我炮制了以下。批判赞赏……
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;
}
为什么要重新发明轮子?试试Lodash吧。它有许多必须的函数,如isEqual()。
_.isEqual(object, other);
它将使用ECMAScript 5和本地优化(如果它们在浏览器中可用)强力检查每个键值——就像本页上的其他示例一样。
注意:以前这个答案推荐使用Underscore.js,但是lodash在修复bug和一致性解决问题方面做得更好。
如果两个对象的所有属性都具有相同的值,并且所有嵌套对象和数组都递归地具有相同的值,那么将它们视为相等是很有用的。我也认为以下两个对象是相等的:
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): 这里是单元测试和测试运行器。
如果您有一个方便的深度复制函数,您可以使用下面的技巧来使用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中已经存在的属性将被简单地覆盖,它们在克隆对象中的顺序将被保留。
我写了一个运行在Node.js和浏览器上的小库,叫做compare.js。它提供了常见的比较运算符,例如==,!=,>,>=,<,<=和所有JavaScript数据类型的标识符。
例如,你可以用
cmp.eq(obj1, obj2);
这将检查是否相等(使用深度相等的方法)。否则,如果你这样做
cmp.id(obj1, obj2);
它将通过引用进行比较,从而检查标识。 您还可以在对象上使用<和>,它们表示子集和超集。
Compare.js被近700个单元测试覆盖,因此它应该不会有太多的bug;-)。
你可以在https://github.com/goloroden/compare.js上免费找到它,它是MIT许可下的开源软件。
判断两个对象是否相似的一个快速“破解”方法是使用它们的toString()方法。如果您正在检查对象A和B,请确保A和B具有有意义的toString()方法,并检查它们返回的字符串是否相同。
这不是灵丹妙药,但有时在正确的情况下是有用的。
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.
这是我的版本。它正在使用new Object。ES5中引入的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 } }));
这是一个非常干净的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"})
下面的一些解决方案在性能、功能和风格方面存在问题……它们没有经过充分的考虑,其中一些在不同的情况下失败了。我试图在自己的解决方案中解决这个问题,我非常感谢您的反馈:
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;
};
我对这个函数做如下假设:
你控制你比较的对象,你只有基本的值(例如。而不是嵌套的对象、函数等)。 您的浏览器支持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;
}
如果你在AngularJS中工作,angular。Equals函数将确定两个对象是否相等。在Ember.js中使用isEqual。
角。equals -关于这个方法的更多信息,请参阅文档或源代码。它还对数组进行了深度比较。 js isEqual -关于这个方法的更多信息,请参阅文档或源代码。它不会对数组进行深度比较。
Var紫色=[{"紫色":"喝了"}]; Var drinking =[{"紫色":"喝了"}]; 如果(角。等于(紫色,喝)){ 文档。写(有dat); } < script src = " https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js " > < /脚本>
为了比较简单的键/值对对象实例的键,我使用:
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;
}
对象是否相等检查:JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())
上面的测试还适用于对象数组,在这种情况下使用http://www.w3schools.com/jsref/jsref_sort.asp中记录的排序函数
对于具有平面JSON模式的小型数组可能足够了。
如果要比较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:{…},设置美元:{…},美元:{…返回}}like对象。
这里有一个非常基本的方法来检查对象的“值是否相等”。
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));
演示 - JSFiddle
如你所见,为了检查对象的“值是否相等”,我们本质上必须遍历对象中的每个属性,以查看它们是否相等。虽然这个简单的实现适用于我们的示例,但有很多情况它无法处理。例如:
如果其中一个属性值本身就是一个对象呢? 如果属性值之一是NaN(中唯一的值 JavaScript不等于它自己?) 如果a有一个值为undefined的属性,而b没有呢 这个属性(因此计算为undefined?)
对于一个健壮的检查对象“值是否相等”的方法,最好依赖于一个经过良好测试的库,它涵盖了各种边缘情况,如下划线。
var john = {
occupation: "Web Developer",
age: 25
};
var bobby = {
occupation: "Web Developer",
age: 25
};
// Outputs: true
console.log(_.isEqual(john, bobby));
演示 - JSFiddle
在Node.js中,你可以使用它的原生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;
};
当然,当我们在它的时候,我会抛出我自己对车轮的重新发明(我为辐条和使用的材料的数量感到自豪):
////////////////////////////////////////////////////////////////////////////////
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,但当然,对于一个差异嵌套很深的大对象,它可能不那么有效。在我自己的场景中,良好地处理嵌套数组非常重要。
希望它能帮助需要这种“轮子”的人。
EDIT: This method is quite flawed, and is rife with its own issues. I don't recommend it, and would appreciate some down-votes! It is problematic because 1) Some things can not be compared (i.e. functions) because they can not be serialized, 2) It isn't a very fast method of comparing, 3) It has ordering issues, 4) It can have collision issues/false positives if not properly implemented, 5) It can't check for "exactness" (===), and instead is based of value equality, which is oftentimes not what is desired in a comparison method.
这个问题的一个简单解决方案是对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传递一个“replace”函数来筛选比较。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;
});
我知道这有点老了,但我想添加一个解决方案,我想出了这个问题。 我有一个对象,我想知道它的数据什么时候改变。类似于Object。我所做的是:
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。
是的,另一个答案……
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>
从我的个人库中取出,我经常用它来工作。下面的函数是一个宽大的递归深度等号,它不检查
阶级平等 继承的值 价值观严格平等
我主要用这个来检查我是否得到了对各种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!"); } })();
我不是Javascript专家,但这里有一个简单的解决方法。我检查三件事:
它是一个对象,而且它不是null,因为typeof null是对象。 如果两个对象的属性计数相同?否则它们就不相等。 遍历一个对象的属性,并检查对应的属性在第二个对象中是否具有相同的值。
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));
下面是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;
}
}
这里有演示
短功能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可比的对象副本:
Var可比= o => (typeof o != '对象' || !o)?o: 种(o) .sort()。减少((c键)= > (c[主要]=可比(o(例子)),c), {}); / /演示: var = {1, c: 4 b:[2、3],d: {e:“5”,f:零}}; var b = {b:[2、3],c: 4 d: {f: null, e:“5”},答:1}; console.log (JSON.stringify(可比(a))); console.log (JSON.stringify(可比(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}}.
我有一个更短的函数,它将深入到所有子对象或数组。它和JSON.stringify(obj1) === JSON.stringify(obj2)一样高效,但是JSON.stringify(obj2)。如果顺序不相同(如此处所述),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();
}
我也遇到了同样的问题,并决定自己编写解决方案。但是因为我也想比较数组和对象,反之亦然,所以我设计了一个通用的解决方案。我决定将函数添加到原型中,但是可以很容易地将它们重写为独立的函数。代码如下:
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"});
函数返回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,即使属性的顺序不同。需要注意的一个小细节:这段代码还检查两个变量的相同类型,因此“3”与3不同。
我的版本,包括发现差异的地方,以及差异是什么。
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');
}
}
只是想利用一些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;
}
)
}
我不知道是否有人发布过类似的东西,但这是我做的一个检查对象相等的函数。
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;
}
而且,它是递归的,所以它也可以检查深度相等,如果你这么称呼它的话。
这是一个通用的相等检查函数,它接收数组元素作为输入,并将它们相互比较。适用于所有类型的元素。
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);
});
这里还有一个要点
我已经实现了一个方法,它接受两个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>
最简单和逻辑的解决方案,比较一切像对象,数组,字符串,Int…
JSON。stringify({a: val1}) == JSON。stringify ({a: val2})
注意:
你需要用你的Object替换val1和val2 对于对象,必须对两侧对象进行递归排序(按键)
下面是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
你可以使用_。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 = { 关键:“价值” }; Var object2 = { 关键:“价值” }; Var object3 = { 键:“无值” }; console.log('object1和object2相等:',JSON.stringify(object1) === JSON.stringify(object2)); console.log('object2和object3相等:',JSON.stringify(object2) === JSON.stringify(object3));
不同。如果对象中键的顺序不重要,我不需要知道所述对象的原型。使用总是有用的。
const object = {};
JSON.stringify(object) === "{}" will pass but {} === "{}" will not
我看到了意大利式的代码答案。 不使用任何第三方的lib,这是非常容易的。
首先,按键对两个对象进行排序。
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)
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));
对于那些使用Node的人来说,在本地util库中有一个叫做isDeepStrictEqual的方便方法可以实现这一点。
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
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
这是一个简单的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
}
ES6:我能做到的最小代码是这样的。它通过对代表对象的所有键值数组进行字符串化来进行递归的深度比较,唯一的限制是没有方法或符号进行比较。
const compareObjects = (a, b) => { let s = (o) => Object.entries(o).sort()。映射(i => { if(i[1]实例对象)i[1] = s(i[1]); 返回我 }) 返回JSON.stringify(s(a)) === JSON.stringify(s(b)) } console.log (compareObjects ({b: 4,答:{b: 1}}, {} {b: 1, b: 4}));
重要提示:这个函数正在执行JSON。stringfy在数组中,并将键排序,而不是在对象本身中:
["a" ["b", 1]] [" b ", 4]
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))
排序对象(字典) 比较JSON字符串 函数areTwoDictsEqual(dictA, dictB) { 函数sortDict(dict) { var keys = Object.keys(dict); keys.sort (); var newDict = {}; For (var i=0;我< keys.length;我+ +){ Var key = keys[i]; Var值= dict[key]; newDict[key] = value; } 返回newDict; } 返回JSON.stringify(sortDict(dictA)) == JSON.stringify(sortDict(dictB)); }
经过这么多的搜索,我找到了以下工作解决方案
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中的对象平等
虽然这个问题已经有很多答案了。我只是想提供另一种实现方法:
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.
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上具有相同的值,Result将为true。
如何确定部分对象(partial <T>)等于typescript中的原始对象(T)。
function compareTwoObjects<T>(original: T, partial: Partial<T>): boolean {
return !Object.keys(partial).some((key) => partial[key] !== original[key]);
}
附注:最初我打算提出一个有答案的新问题。但这样的问题已经存在,并被标记为重复题。
我写这个方法只是为了确保数组和对象都能被清晰地比较。
这应该也能做到!:)
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)
}
}
}
}
这个问题已经有30多个答案了。我将总结并解释它们(用“我父亲”的比喻),并添加我建议的解决方案。
你有4+1类解
1)使用不完整的简单语句
如果你赶时间,那么99%的正确性都可以。
例如,Pratik Bhalodiya建议的JSON.stringify()或JSON。encode,或. tostring(),或其他方法将对象转换为String,然后逐个字符使用===比较两个String。
然而,缺点是在String中没有Object的全局标准唯一表示。例如:{a: 5, b: 8}和{b: 8和a: 5}是相等的。
优点:快,快。 缺点:希望有效!如果环境/浏览器/引擎记住了对象的顺序(例如Chrome/V8),并且键的顺序不同(感谢Eksapsy),它将无法工作。所以,完全不能保证。在大型对象中,性能也不会很好。
我的父亲类比
当我在谈论我的父亲时,“我高大英俊的父亲”和“我高大英俊的父亲”是同一个人!但这两个弦是不一样的。
请注意,在英语语法中,形容词的顺序其实是正确的(标准的方式),它应该是“英俊的高个子男人”,但如果你盲目地认为iOS 8 Safari的Javascript引擎也遵循同样的语法,你就是在拿自己的能力冒险!# WelcomeToJavascriptNonStandards
2)自己DIY编写递归函数
如果你正在学习,这很好。
例子是atmin的解。
最大的缺点是你肯定会错过一些边缘情况。您考虑过对象值中的自引用吗?你考虑过NaN吗?您是否考虑过具有相同ownProperties但不同原型父对象的两个对象?
我只鼓励人们在进行实践并且代码不会投入生产的情况下这样做。这是唯一一种重新发明轮子有正当理由的情况。
优点:学习机会。 缺点:不可靠。需要时间和精力。
我的父亲类比
这就像假设我爸爸的名字是“约翰·史密斯”,他的生日是“1/1/1970”,那么任何一个名字是“约翰·史密斯”,出生在“1/1/1970”的人就是我的父亲。
通常情况下是这样,但如果有两个“约翰·史密斯”在那一天出生呢?如果你认为你会考虑他们的身高,那么这就提高了准确性,但仍然不是一个完美的比较。
2.1你的范围有限DIY比较器
与其疯狂地递归检查所有属性,不如考虑只检查“有限”数量的属性。例如,如果对象是Users,您可以比较它们的emailAddress字段。
它仍然不是一个完美的解决方案,但比解决方案#2的好处是:
它是可预测的,而且不太可能崩溃。 您正在推动平等的“定义”,而不是依赖于对象的原始形式和形状及其原型和嵌套属性。
3)使用功能相同的图书馆版本
如果您需要生产级别的质量,并且您不能更改系统的设计,则很好。
例如_。等于lodash,已经在coolaj86的回答中,或者在Tony Harvey的回答中提到的Angular的回答中,或者在Node的Rafael Xavier的回答中。
优点:其他人都这么做。 缺点:外部依赖,这可能会花费你额外的内存/CPU/安全问题,甚至一点点。此外,仍然可以错过一些边缘情况(例如,是否两个对象具有相同的ownProperties但不同的原型父应该被认为是相同的。)最后,你可能无意中解决了一个潜在的设计问题;只是说!
我的父亲类比
这就像付钱给中介机构,根据他的电话、姓名、地址等找到我的生父。
这会花更多的钱,而且可能比我做背景调查更准确,但不能涵盖边缘情况,比如我父亲是移民/庇护者,他的生日是未知的!
4)在对象中使用标识符
如果你仍然可以改变系统(你正在处理的对象)的设计,并且你希望你的代码能够持久,那就太好了。
它并不适用于所有情况,而且性能可能不太好。然而,这是一个非常可靠的解决方案,如果你能做到的话。
解决方案是,系统中的每个对象都有一个唯一的标识符以及所有其他属性。标识符的唯一性将在生成时得到保证。在比较两个对象时,您将使用这个ID(也称为UUID/GUID—全局/通用唯一标识符)。即,当且仅当这些id相等时,它们相等。
id可以是简单的自动增量数字,也可以是通过库生成的字符串(建议)或一段代码。所有你需要做的是确保它总是唯一的,在auto_incremental的情况下,它可以是内置的,或者在UUID的情况下,可以检查是否所有现有的值(例如MySQL的unique列属性)或简单地(如果来自库)依赖于给出极低的冲突可能性。
注意,您还需要始终将ID存储在对象中(以保证其唯一性),并且实时计算它可能不是最好的方法。
优点:可靠,高效,不脏,现代。 缺点:需要额外的空间。可能需要重新设计一下系统。
我的父亲类比
我父亲的社保号是911-345-9283,所以有这个社保号的人就是我父亲,任何自称是我父亲的人也一定有这个社保号。
结论
就准确性和可靠性而言,我个人更喜欢解决方案#4 (ID)。如果不可能,我会选择2.1,因为它具有可预测性,然后是3。如果两者都不可能,选择第二条,最后是第一条。
另外一个选项是使用Ramda库的equals:
const c = {a: 1, b: 2};
const d = {b: 2, a: 1};
R.equals(c, d); //=> true
Const obj = { 名称:“卡尔”, 年龄:15 } Const obj2 = { 名称:“卡尔”, 年龄:15日 } const compareObj = (objects) => { Const res = objects.map((item) => { 返回Object.entries(项).flat . join () () }) 返回res.every((a) => { 返回a === res[0] }) } console.log (compareObj (obj, methoda ()))
如果你真的想比较并返回两个对象的差值。 您可以使用这个包:https://www.npmjs.com/package/deep-diff
或者只使用这个包使用的代码
https://github.com/flitbit/diff/blob/master/index.js
只是不要把它转换成字符串进行比较。
下面是一个使用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;
}
}
};
下面是一个使用JSON的简短实现。stringify但按@Jor建议的方式对键进行排序。
一些测试来自@EbrahimByagowi的回答。
当然,通过使用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}}));
虽然这个问题已经得到了充分的回答,但我还缺少一种方法:toJSON接口。
通常你想通过字符串化来比较object,因为这是最快的方法。但往往比较被认为是假的,因为性质的顺序。
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的字符串表示被认为是相同的。
TIP
如果你没有直接生成对象的权限,你可以简单地附加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
我之前添加了一个答案,但它不是完美的,但这个将检查对象的相等性
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});
在对象中(没有方法),我们需要检查嵌套的对象,数组和基本类型。对象可以有其他对象和数组(数组也可以包括其他对象和数组),所以我们可以使用如下所示的递归函数: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中,你可以使用' React -fast-compare'中的isEqual。这个答案可能不适用于纯JavaScript,但在使用React时可能有用。
console.log(isEqual({ hello: 'world' }, { hello: 'world' })) // returns true
React中最快的深度相等比较。非常快速的通用深度比较。非常适合React。备忘录和shouldComponentUpdate。
更多信息可以在这里找到:https://www.npmjs.com/package/react-fast-compare。
纯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))
let std1 = {
name: "Abhijeet",
roll: 1
}
let std2 = {
name: "Siddharth",
roll: 2
}
console.log(JSON.stringify(std1) === JSON.stringify(std2))
如果你的问题是检查两个对象是否相等,那么这个函数可能会有用
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
}
first we check if the length of the list of keys of these objects is the same, if not we return false to check if two objects are equal they must have the same keys(=names) and the same values of the keys, so we get all the keys of objA, and objB and then we check if they are equal once we find that tow keys are not equal then we return false and then when all the keys are equal then we loop through one of the keys of one of the objects and then we check if they are equal once they are not we return false and after the two loops finished this means they are equal and we return true NOTE: this function works with only objects with no functions
使用JSON.stringify()并不总是可靠的。所以在我看来,这种方法是你问题的最佳解决方案
首先,不,没有通用的方法来确定一个对象是否相等!
但是有一个概念叫做浅平等比较。 有一个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 methoda)
shallowEqual在两个值(即obj1和obj2)之间进行浅相等比较,以确定它们是否相等。
相等性是通过迭代给定obj1上的键来执行的,并且当任何键的值在obj1和obj2之间不严格相等时返回false。否则,只要所有键的值严格相等,就返回true。
我发现比较两个javascript对象的值而忽略属性顺序的一个简单方法是使用JSON stringify replacer函数:
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);
这将在每一步对属性进行排序,以便字符串结果对属性顺序不变。以前可能有人这样做过,但我只是想分享一下,以防万一:)。
Stringify两个对象并进行比较
返回(JSON.stringify(obj1) === = JSON.stringify(obj2))
这将返回true或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
}
你也可以使用这个方法来获取obj1和obj2之间的Diff对象。
看看这个答案的细节:两个对象之间的一般深度差异
简单来说:
const compare = (x, y) => {
const srt = (obj) => JSON.stringify(obj)?.split('').sort().join('');
return srt(x) === srt(y);
};
// ----- How to use ---
const a = {'one':1, 'two':2,'three':3};
const b = {'two':2, 'one':1, 'three':3}; //note same values as (const a)
const c = {'one':1, 'two':2,'three':3};
const d = {'one':1, 'two':2,'four':4};
compare(a, b); //true
compare(a, c); //true
compare(a, d); //false
//----- BUT! -----
JSON.stringify(a) === JSON.stringify(b); //false
//----- AND -----
compare({}, {}); //true
compare({}, undefined); //false
compare(undefined, undefined); //true
compare(undefined, ''); //false
compare(undefined, null); //false
compare(null, null); //true
compare('', ''); //true
const isEqual = function (var1, var2) {
if (typeof var1 === 'object' && typeof var2 === 'object') {
// Checking equality for each of the inner values of the objects
const keys = [...new Set([...Object.keys(var1),...Object.keys(var2)])];
return keys.every(key => isEqual(var1[key], var2[key]) && isEqual(var2[key], var1[key]));
} else { // Primitive types (number, boolean etc..)
return var1 === var2; // Normal equality
}
}
我在这里看到了很多很好的答案,但我找不到一个简短的函数,所以我创建了一个。 这将检查两个变量是否相等,无论其类型如何。 希望你看得合适。
简而言之,这将检查两个变量是否相等,无论其类型如何。
function isSame (obj1, obj2) {
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
return obj1Keys.length === obj2Keys.length && obj1Keys.every((key) => obj1[key] === obj2[key])
}