All of these answers will fail if a path in the target is null, and thus it is necessary to look 1 level deeper into the object before recursing and check if a target node will be null, and if so update it before recursing.
NOTE: My answer takes the arguments in a different order than everyone else. a is the item you are checking for missing keys, and b is the reference object with correct keys. my function also returns true/false as to whether any changes were made to a. it also copies over default values from b to a when key is missing.
function has (o,k) {
return typeof o==='object'&&(o.hasOwnProperty(k)||o[k]!==undefined);
}
function recursiveCheck( a, b ) {
var c = false;//whether we have changes. the calling scope wants to know
// a is the object to check, b is the target
for(let k in b) {
if(!has(a,k)) {
c=true;
a[k] = b[k];
}else{
// if b[k] is an object, we recursively check on the children for changes
// we only want to manipulate a[k] if b[k]=== object and not null!!!
if(typeof b[k]==='object'&&b[k]!==null){
for(let k2 in b[k]) {
if(a[k]===null){
a[k]=b[k];
c=true;
}
if( (!has(a[k],k2) || a[k][k2]===null) && typeof b[k][k2]==='object' && b[k][k2]!==null){
a[k][k2] = b[k][k2];
c=true;
}
}
const hasChange = recursiveCheck(a[k],b[k]);
c = c || hasChange;
}
}
}
return c;
}
usage
var a = { foo: null, bar: true };//this is the object we are checking
var b = { foo:{test:1,hello:false},bar: false}; //this is the object with correct keys and default values that should copy over to a when needed.
const didChange = recursiveCheck(a,b);
test cases that prove correct output
var target = {
private:{
has:false,
cost:0
},
shared:{
has:false,
cost:0
}
}
var inputs = [
{},// true
{private:null,shared:null},// true
{private:{has:true},shared:{cost:50}},// true
{private:{has:true,cost:500},shared:{has:false,cost:250}}// false
];
console.log('inputs---',inputs);
const results = inputs.map(item=>recursiveCheck(item,target));
console.log('inputs(transformed)---',inputs);
console.log('results---',results); // should be [true, true, true, false]