如何检查两个对象具有相同的属性名称集?

我在应用程序中使用 node、 mocha 和 chai。我想测试我返回的结果数据属性是否与我的模型对象(非常类似于 chai 的实例)相同的“对象类型”。我只是想确认这两个对象具有相同的属性名称集。我对这些房产的实际价值特别不感兴趣。

假设我有下面这个模型。我想要检查我的 result. data 是否具有与预期模型相同的所有属性。因此在本例中,Person 具有 firstName 和 lastName。

因此,如果 results.data.lastNameresults.data.firstName都存在,那么它应该返回 true。如果其中一个不存在,它应该返回 false。一个额外的好处是,如果 result. data 有任何其他属性,比如 result. data.name,那么它将返回 false,因为 Person 中不存在 name。

这个模型

function Person(data) {
var self = this;
self.firstName = "unknown";
self.lastName = "unknown";


if (typeof data != "undefined") {
self.firstName = data.firstName;
self.lastName = data.lastName;
}
}
150147 次浏览

可以序列化简单数据以检查是否相等:

data1 = {firstName: 'John', lastName: 'Smith'};
data2 = {firstName: 'Jane', lastName: 'Smith'};
JSON.stringify(data1) === JSON.stringify(data2)

这会给你一些像

'{firstName:"John",lastName:"Smith"}' === '{firstName:"Jane",lastName:"Smith"}'

作为一种功能。

function compare(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
compare(data1, data2);

剪辑

如果你像你说的那样使用印度茶,看看 http://chaijs.com/api/bdd/#equal-section

编辑2

如果你只是想检查钥匙..。

function compareKeys(a, b) {
var aKeys = Object.keys(a).sort();
var bKeys = Object.keys(b).sort();
return JSON.stringify(aKeys) === JSON.stringify(bKeys);
}

应该可以。

如果要检查两个对象是否具有相同的属性名,可以这样做:

function hasSameProps( obj1, obj2 ) {
return Object.keys( obj1 ).every( function( prop ) {
return obj2.hasOwnProperty( prop );
});
}


var obj1 = { prop1: 'hello', prop2: 'world', prop3: [1,2,3,4,5] },
obj2 = { prop1: 'hello', prop2: 'world', prop3: [1,2,3,4,5] };


console.log(hasSameProps(obj1, obj2));

通过这种方式,确保只检查两个对象的可迭代和可访问属性。

编辑-2013.04.26:

以前的函数可以按以下方式重写:

function hasSameProps( obj1, obj2 ) {
var obj1Props = Object.keys( obj1 ),
obj2Props = Object.keys( obj2 );


if ( obj1Props.length == obj2Props.length ) {
return obj1Props.every( function( prop ) {
return obj2Props.indexOf( prop ) >= 0;
});
}


return false;
}

通过这种方式,我们检查两个对象具有相同数量的属性(否则对象没有相同的属性,我们必须返回一个逻辑假) ,然后,如果数字匹配,我们去检查它们是否具有相同的属性。

意外收获

一个可能的改进是引入类型检查,以便在每个属性上强制执行匹配。

2这里有一个简短的 ES6可变版本:

function objectsHaveSameKeys(...objects) {
const allKeys = objects.reduce((keys, object) => keys.concat(Object.keys(object)), []);
const union = new Set(allKeys);
return objects.every(object => union.size === Object.keys(object).length);
}

A little 性能测试(MacBook Pro-2,8 GHz Intel Core i7,Node 5.5.0) :

var x = {};
var y = {};


for (var i = 0; i < 5000000; ++i) {
x[i] = i;
y[i] = i;
}

结果:

objectsHaveSameKeys(x, y) // took  4996 milliseconds
compareKeys(x, y)               // took 14880 milliseconds
hasSameProps(x,y)               // after 10 minutes I stopped execution

下面是我对 JSON 属性的验证尝试。我使用了@casey-Foster 的方法,但是增加了递归来进行更深层次的验证。函数中的第三个参数是可选的,仅用于测试。

//compare json2 to json1
function isValidJson(json1, json2, showInConsole) {


if (!showInConsole)
showInConsole = false;


var aKeys = Object.keys(json1).sort();
var bKeys = Object.keys(json2).sort();


for (var i = 0; i < aKeys.length; i++) {


if (showInConsole)
console.log("---------" + JSON.stringify(aKeys[i]) + "  " + JSON.stringify(bKeys[i]))


if (JSON.stringify(aKeys[i]) === JSON.stringify(bKeys[i])) {


if (typeof json1[aKeys[i]] === 'object'){ // contains another obj


if (showInConsole)
console.log("Entering " + JSON.stringify(aKeys[i]))


if (!isValidJson(json1[aKeys[i]], json2[bKeys[i]], showInConsole))
return false; // if recursive validation fails


if (showInConsole)
console.log("Leaving " + JSON.stringify(aKeys[i]))


}


} else {


console.warn("validation failed at " + aKeys[i]);
return false; // if attribute names dont mactch


}


}


return true;


}

如果您想要像@speculees 这样的深度验证,这里有一个使用 deep-keys的答案(公开: 我是这个小包的维护者)

// obj1 should have all of obj2's properties
var deepKeys = require('deep-keys');
var _ = require('underscore');
assert(0 === _.difference(deepKeys(obj2), deepKeys(obj1)).length);


// obj1 should have exactly obj2's properties
var deepKeys = require('deep-keys');
var _ = require('lodash');
assert(0 === _.xor(deepKeys(obj2), deepKeys(obj1)).length);

chai:

var expect = require('chai').expect;
var deepKeys = require('deep-keys');
// obj1 should have all of obj2's properties
expect(deepKeys(obj1)).to.include.members(deepKeys(obj2));
// obj1 should have exactly obj2's properties
expect(deepKeys(obj1)).to.have.members(deepKeys(obj2));

如果您正在使用 underscoreJs,那么您可以简单地使用 _. isequals 函数 它比较所有的键和值在每一个层次结构的每一个级别,如下面的例子。

var object = {"status":"inserted","id":"5799acb792b0525e05ba074c","data":{"workout":[{"set":[{"setNo":1,"exercises":[{"name":"hjkh","type":"Reps","category":"Cardio","set":{"reps":5}}],"isLastSet":false,"index":0,"isStart":true,"startDuration":1469689001989,"isEnd":true,"endDuration":1469689003323,"speed":"00:00:01"}],"setType":"Set","isSuper":false,"index":0}],"time":"2016-07-28T06:56:52.800Z"}};


var object1 = {"status":"inserted","id":"5799acb792b0525e05ba074c","data":{"workout":[{"set":[{"setNo":1,"exercises":[{"name":"hjkh","type":"Reps","category":"Cardio","set":{"reps":5}}],"isLastSet":false,"index":0,"isStart":true,"startDuration":1469689001989,"isEnd":true,"endDuration":1469689003323,"speed":"00:00:01"}],"setType":"Set","isSuper":false,"index":0}],"time":"2016-07-28T06:56:52.800Z"}};


console.log(_.isEqual(object, object1));//return true

如果这两个对象中的所有键和值都相同,那么它将返回 true,否则返回 false。

下面是 Schirrmacher提供的函数的深度检查版本。 以下是我的尝试,请注意:

  • 解决方案不检查是否为空,并且不能防弹
  • 我还没有对它进行性能测试。也许 schirrmacher 或 OP 可以做到这一点,并为社区分享。
  • 我不是 JS 专家:)。
function objectsHaveSameKeys(...objects) {
const allKeys = objects.reduce((keys, object) => keys.concat(Object.keys(object)), [])
const union = new Set(allKeys)
if (union.size === 0) return true
if (!objects.every((object) => union.size === Object.keys(object).length)) return false


for (let key of union.keys()) {
let res = objects.map((o) => (typeof o[key] === 'object' ? o[key] : {}))
if (!objectsHaveSameKeys(...res)) return false
}
return true
}

更新1

在我的计算机上,通过跳过 concat()并直接将键添加到 Set(),可以实现对递归深度检查版本90% 的改进。相同的优化,以原来的单一水平的版本也达到约40% 的改善。

优化的深度检查现在在性能上非常类似于优化的单级版本!

function objectsHaveSameKeysOptimized(...objects) {
let union = new Set();
union = objects.reduce((keys, object) => keys.add(Object.keys(object)), union);
if (union.size === 0) return true
if (!objects.every((object) => union.size === Object.keys(object).length)) return false


for (let key of union.keys()) {
let res = objects.map((o) => (typeof o[key] === 'object' ? o[key] : {}))
if (!objectsHaveSameKeys(...res)) return false
}
return true
}

性能比较

var x = {}
var y = {}
var a = {}
for (var j = 0; j < 10; ++j){
a[j] = j
}


for (var i = 0; i < 500000; ++i) {
x[i] = JSON.parse(JSON.stringify(a))
y[i] = JSON.parse(JSON.stringify(a))
}


let startTs = new Date()
let result = objectsHaveSameKeys(x, y)
let endTs = new Date()
console.log('objectsHaveSameKeys = ' + (endTs - startTs)/1000)

结果

A: 递归/深度检查版本 *

  1. ObjectsHaveSameKeys = 5.185
  2. ObjectsHaveSameKeysOptimized = 0.415

B: 原始的非深度版本

  1. ObjectsHaveSameKeysOriginalNonDeep = 0.517
  2. ObjectsHaveSameKeysOriginalNonDeepOptimized = 0.342