比较对象结构



我有一个像这样的对象:

const obj = {
name: "one",
created: "2022-05-05T00:00:00.0Z",
something: {
here: "yes"
}
}

我可以逐个检查它是否具有某些属性:

assert( obj.name !== undefined && obj.name === "one")
assert( obj.something !== undefined && obj.something.here !== undefined && obj.something.here === "yes")

是否有一种方法来实现相同的传递另一个对象进行比较?另一个对象将具有更少的值(在本例中,我不关心created属性是否存在)

类似:


const obj = {
name: "one",
created: "2022-05-05T00:00:00.0Z",
something: {
here: "yes"
}
}
hasAllOf(obj, {
name: "one",
something: {
here: "yes"
}
}) // returns true

使用lodash或其他库吗?

您可以使用类型检查助手is-

编写递归hasAllOf

const is = (t, T) =>
t?.constructor === T
const hasAllOf = (a, b) =>
is(a, Object) && is(b, Object) || is(a, Array) && is(b, Array)
? Object.keys(b).every(k => hasAllOf(a[k], b[k]))
: a === b
const obj = {
name: "one",
created: "2022-05-05T00:00:00.0Z",
something: {
here: "yes",
and: ["this", 2, { f: "three" }]
}
}
console.log(
hasAllOf(obj, {
name: "one",
something: {
here: "yes",
and: ["this", 2, { f: "three" }]
}
})
)

通过使用另一个条件

支持MapSet

const is = (t, T) =>
t?.constructor === T
const hasAllOf = (a, b) => {
switch (true) {
case is(a, Object) && is(b, Object) || is(a, Array) && is(b, Array):
return Object.keys(b).every(k => hasAllOf(a[k], b[k]))
case is(a, Set) && is(b, Set):
return [...b].every(v => a.has(v))
case is(a, Map) && is(b, Map):
return [...b.keys()].every(k => hasAllOf(a.get(k), b.get(k)))
default:
return a === b
}
}
const obj = {
name: new Map([[1, "one"], [2, "two"]]),
created: "2022-05-05T00:00:00.0Z",
something: {
here: new Set("yes", "ok"),
and: ["this", 2, { f: "three" }],
}
}
console.log(
hasAllOf(obj, {
name: new Map([[1, "one"]]),
something: {
here: new Set("yes"),
and: ["this", 2, { f: "three" }]
}
})
)

lodash isEqual函数可能就是您正在寻找的。https://lodash.com/docs/isEqual

const obj = {
name: "one",
created: "2022-05-05T00:00:00.0Z",
something: {
here: "yes"
}
}
_.isEqual(delete obj.created, delete {
name: "one",
created: "2022-05-05T00:00:00.0Z",
something: {
here: "yes"
}}.created); //returns true

const obj = {
name: "one",
created: "2022-05-05T00:00:00.0Z",
something: {
here: "yes"
}
}
delete obj.created;
_.isEqual(obj, {
name: "one",
something: {
here: "yes"
}}.created); //returns true

你甚至可以使用不同的lodash函数(_.cloneDeep)来复制原始的,这样你就不会改变它了。

function hasAllOf(obj,data) {
let obj_keys = Object.keys(obj)
for (let key in data) {
if (!(obj_keys.includes(key))) {
return false;
}
if (typeof data[key] === "object" && typeof obj[key] === "object") 
{
let res = hasAllOf(obj[key], data[key])
if (!res) {
return res;
}
res = hasAllOf(data[key], obj[key])
if (!res) {
return res;
}
}
else if (data[key] !== obj[key])
{
return false;
}
}
return true;
}



const obj = 
{
name: "one",
created: "2022-05-05T00:00:00.0Z",
something: 
{
here: "yes",


}
}

const data = 
{
name: "one",
something:
{
here: "yes",
},
}

console.log(hasAllOf(obj, data))

我认为_。consto与我所寻找的非常匹配,它有点冗长,但比逐一进行所有比较(特别是在较大的对象中)要好得多,也更清晰


_.conformsTo(obj, {
name: (n) => n === "one",
created: (c) => _.isString(c),
// calling _conformsTo on nested objects
something: (s) =>  _.conformsTo(s, {
here: (h) => h === "yes"
})
}) 

更新…

我甚至可以创建一个isconformsTo函数,返回一个"部分应用"。函数,使其更清晰。

完整的示例:


const _ = require('lodash')
const is = (expected) => (actual) => actual === expected
const conformsTo = (template) => (actual) => _.conformsTo(actual, template)
_.conformsTo(
{ // source
name: "one",
created: "2022-05-05T00:00:00.0Z",
something: {
here: "yes"
}
}, 
{ // "spec"
name: is('one'),
something: conformsTo({
here: is("yes")
})
}
)

最新更新