JavaScript: Deep check objects have same keys
I'd do a recursive check if a property's value is an object.
There's an interesting wrinkle here; actually, there are (at least) two:
- What if one of the "objects" is
null
and the other has no properties?true
orfalse
? - What if one of the objects has
{a: null}
and the other has{a: 17}
?true
orfalse
? - What if one of the objects has
{a: null}
and the other has{a: {}}
?true
orfalse
?
For the purposes of this example, I've treated null
like an object with no properties, but it's very much dependent on your use case. I can think of at least two other ways to go (null
doesn't match anything but null
, or null
doesn't match anything but a non-object, even if the object has no own properties) and there are probably others.
See comments:
const deepSameKeys = (o1, o2) => {
// Both nulls = yes
if (o1 === null && o2 === null) {
return true;
}
// Get the keys of each object
const o1keys = o1 === null ? [] : Object.keys(o1);
const o2keys = o2 === null ? [] : Object.keys(o2);
if (o1keys.length !== o2keys.length) {
// Different number of own properties = not the same
return false;
}
// At this point, one of two things is true:
// A) `o1` and `o2` are both `!null`, or
// B) One of them is `null` and the other has own "own" properties
// The logic below relies on the fact we only try to use `o1` or
// `o2` if there's at least one entry in `o1keys`, which we won't
// given the guarantee above.
// Handy utility function
const hasOwn = Object.prototype.hasOwnProperty;
// Check that the keys match and recurse into nested objects as necessary
return o1keys.every(key => {
if (!hasOwn.call(o2, key)) {
// Different keys
return false;
}
// Get the values and their types
const v1 = o1[key];
const v2 = o2[key];
const t1 = typeof v1;
const t2 = typeof v2;
if (t1 === "object") {
if (t2 === "object" && !deepSameKeys(v1, v2)) {
return false;
}
}
if (t2 === "object") {
if (t1 === "object" && !deepSameKeys(v1, v2)) {
return false;
}
}
return true;
});
};
// Checking your example
const objOne = {"a": "one", "b": "two", "c": {"f": "three_one"}};
const objTwo = {"a": "four", "b": "five", "c": {"f": "six_one"}};
const objThree = {"a": "four", "b": "five", "c": {"g": "six_one"}};
console.log("objOne vs. objTwo: ", deepSameKeys(objOne, objTwo)); // true
console.log("objTwo vs. objThree: ", deepSameKeys(objTwo, objThree)); // false
// `null` checks
console.log("{a: null} vs. {a: 17} ", deepSameKeys({a: null}, {a: 17})); // true
console.log("{a: null} vs. {a: {}} ", deepSameKeys({a: null}, {a: {}})); // true -- depending on your use case, you may want this to be false
console.log("{a: null} vs. {a: {x:1}} ", deepSameKeys({a: null}, {a: {x:1}})); // false
// Differing value type check
console.log("{a: 1} vs. {a: '1'}} ", deepSameKeys({a: 1}, {a: '1'})); // true