如何检查两个对象是不是具有相同的一组属性名称?
Posted
技术标签:
【中文标题】如何检查两个对象是不是具有相同的一组属性名称?【英文标题】:How can I check that two objects have the same set of property names?如何检查两个对象是否具有相同的一组属性名称? 【发布时间】:2012-12-31 08:57:27 【问题描述】:我在我的应用程序中使用 node、mocha 和 chai。我想测试我返回的结果数据属性是否与我的模型对象之一相同的“对象类型”(非常类似于 chai 的实例)。我只是想确认这两个对象具有相同的属性名称集。 我对属性的实际值不感兴趣。
假设我有如下的模型 Person。我想检查我的 results.data 是否具有与预期模型相同的所有属性。所以在这种情况下,Person 具有 firstName 和 lastName。
所以如果results.data.lastName
和results.data.firstName
都存在,那么它应该返回true。如果其中任何一个都不存在,它应该返回 false。如果 results.data 有任何额外的属性,比如 results.data.surname,那么它会返回 false,因为 surname 在 Person 中不存在。
此型号
function Person(data)
var self = this;
self.firstName = "unknown";
self.lastName = "unknown";
if (typeof data != "undefined")
self.firstName = data.firstName;
self.lastName = data.lastName;
【问题讨论】:
【参考方案1】:您可以序列化简单数据以检查是否相等:
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);
编辑
如果您像您说的那样使用 chai,请查看 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);
应该这样做。
【讨论】:
我不想检查属性的实际值,只检查属性名称。很抱歉造成混乱 这正是我一直在寻找的...... JS 新手,不知道如何进行属性反射。谢谢! + 1 表示想法,但要注意陷阱 - 参数顺序很重要在您的方法中:JSON.stringify(b:1, a:1)
不同于 JSON.stringify(a:1, b:1)
它现在可以工作,因为大多数浏览器都为对象键维护某种排序,但 ecma 规范并不要求它,所以这段代码可能会失败。
如果需要深度检查/嵌套对象***.com/questions/41802259/…【参考方案2】:
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);
一点性能测试(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
【讨论】:
真棒比较! 为什么我被否决了?请写评论,以便我改进我的答案:) 为了返回不同键的数量:return objects.reduce((res, object) => res += union.size - Object.keys(object).length, 0);
【参考方案3】:
如果要检查两个对象是否具有相同的属性名称,可以这样做:
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;
通过这种方式,我们检查两个对象具有相同数量的属性(否则对象没有相同的属性,我们必须返回逻辑假)然后,如果数字匹配,我们去检查它们是否具有相同的属性。
奖金
一个可能的增强可能是引入类型检查以强制匹配每个属性。
【讨论】:
我认为这也可以。与凯西的非常相似。谢谢 这不是只检查obj2
有obj1
的属性,反之亦然吗?
该函数检查obj1
的所有属性是否都存在于obj2
中,因此它们具有相同的属性。但反之亦然。如果您想跳过具有不同属性数量的对象的迭代,则必须添加对两个对象中的属性数量的检查,并在它们不匹配的情况下返回逻辑 false。
好像只检查一级属性吧?
@Mirko 是的。请注意,检查是通过在对象中查找相同的键来完成的。它不是基于它们的有效值。 (例如,我可以有两个 name
键,一个分配给一个字符串,一个分配给一个数字,并且检查仍然会返回真实性)。但是,您可以通过在对象键的情况下实现某种可恢复性来适应它,但它需要扩展对数据类型的检查。【参考方案4】:
如果你想要像 @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));
【讨论】:
【参考方案5】:这是schirrmacher 提供的上述函数的深度检查版本。 下面是我的尝试。请注意:
解决方案不检查 null 且不防弹 我没有对它进行性能测试。也许 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% 的改进。 schirrmacher 对原始单级版本的优化也实现了约 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:递归/深度检查版本*
-
objectsHaveSameKeys = 5.185
objectsHaveSameKeysOptimized = 0.415
B:原始非深度版本
-
objectsHaveSameKeysOriginalNonDeep = 0.517
objectsHaveSameKeysOriginalNonDeepOptimized = 0.342
【讨论】:
我喜欢这个,我能看到的唯一改进是在递归之前检查 falsy:if (!res[0]) continue
在 if (!objectsHaveSameKeys(...res)) return false
之前
@AlbertoSadoc,感谢您的建议!条件if(!res[0])
在代码中永远不会为真。但是,如果我们filter()
res,那么它应该可以工作,即res = res.filter((e) => (Object.keys(e).length !== 0))
。但是 filter() 和 Object.keys() 的成本是不合理的,因为我们在递归调用上执行另一个 Object.keys() 无论如何都会使大多数调用的成本增加一倍,只是为了节省一个退出场景的成本。而且额外的代码也不值得。【参考方案6】:
如果你使用 underscoreJs 那么你可以简单地使用 _.isEqual 函数 它会比较每个层次结构级别的所有键和值,如下例所示。
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。
【讨论】:
这里提出的问题是仅检查两个对象的键。这些值无关紧要。您的解决方案检查键和值。【参考方案7】:这是我验证 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;
【讨论】:
OP 的问题是关于比较键和仅键。如果 值 不同,您的代码将在某些情况下报告不等式。例如isValidJson(a: a: 1, a: 1, true)
会抱怨,因为a
是第二个对象中的原始值。此外,您的算法不是可交换的。 (翻转我之前代码中的两个对象,您的代码报告 true
而不是 false
!)以上是关于如何检查两个对象是不是具有相同的一组属性名称?的主要内容,如果未能解决你的问题,请参考以下文章
检查是不是已创建另一个具有相同名称的 DataFrame。错误:“str”对象没有属性“append”
检查自定义对象列表是不是与 Java 8 中的属性具有相同的值