0

How can I loop nested objects if I don't know how much children they have?

For instance, if I wanted to update an old object with another object (code sample), I'll need to go through every node in the objects, to check the keys/values match, one by one.

var savedData = {
  "a": {
    "x": {
      "foo": 1,
      "foofoo": 11
    },
    "y": {
      "bar": 2,
      "barbar": 22
    }
  },
  "b": {
    //...
  },
  "c": {
    //...
  }
};


var newData = {
  "a": {
    "x": {
      "foo": 7 //<== new value to be changed;
      //<== notice there were "foofoo" key here, we need to keep this key and does NOT remove it;
    },
    "y": {
      "bar": 8 //<== new value to be changed;
      //<== notice there were "barbar" key here, we need to keep this key and does NOT remove it;
    },
    "z": { //<== this is a brand new child object to be added to the savedData;
      "baz": 9
    }
  }
};


updateSavedData(newData);


function updateSavedData(newData) {

  if (savedData) {
    Object.keys(savedData).forEach(function(savedKeyLevel1) {
      Object.keys(newData).forEach(function(newKeyLevel1) {

        if (savedKeyLevel1 === newKeyLevel1) {
          console.log('the key [' + savedKeyLevel1 + '] exist among the saved and the new data!');
          if (jQuery.type(savedData[savedKeyLevel1]) === "object") {
            console.log('the key [' + savedKeyLevel1 + '] is an object!');
            //start looping again, but this time in a deeper level of this child object...
            Object.keys(savedData[savedKeyLevel1]).forEach(function(savedKeyLevel2) {
              Object.keys(newData[newKeyLevel1]).forEach(function(newKeyLevel2) {
                if (savedKeyLevel2 === newKeyLevel2) {
                  console.log('the key [' + savedKeyLevel1 + ' -> ' + savedKeyLevel2 + '] exist among the saved and the new data!');
                  //...
                  //<== my question is about what to do here?
                  //if I don't know how much deeper the savedData is.
                } else {
                  //this is a brand new child object, add it to the tree;
                  savedData[savedKeyLevel1][newKeyLevel2] = newData[newKeyLevel1][newKeyLevel2];
                }
              });
            });

          }
        } else {
          //this is a brand new child object, add it to the tree;
          savedData[newKeyLevel1] = newData[newKeyLevel1];
        }
      });
    });

  } else {
    savedData = newData;
  }

  console.log('The savedData after update is:\n', JSON.stringify(savedData));

}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

My question is:

What if I don't know how much deeper the objects are?

What am I looking for here to read/try?

user7607751
  • 465
  • 7
  • 27
  • are you aware of JSON patching? a standard way to do something similar: http://jsonpatch.com/ – Benjaco Nov 11 '18 at 23:26
  • Possible duplicate of [How to deep merge instead of shallow merge?](https://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge) – shkaper Nov 11 '18 at 23:30

2 Answers2

1

You may be making this a little more complicated than it needs to be. So long as it's a simple object with nested objects and properties, you can just test for an object and recurse:

var savedData = {"a": {"x": {"foo": 1,"foofoo": 11},"y": {"bar": 2,"barbar": 22}},"b": {},"c": {}};
var newData = {"a": {"x": {"foo": 7},"y": {"bar": 8 },"z": { "baz": 9}}};
  
function merge(data, newdata){
   for (let key in newdata){
      if (typeof data[key] == 'object')  merge(data[key], newdata[key])
      else data[key] = newdata[key]
    }
}
merge(savedData, newData)
console.log(savedData)
  
Mark
  • 90,562
  • 7
  • 108
  • 148
  • This is the first time I came across or clearly notice calling a function from inside itself! Thank you Mark :) – user7607751 Nov 11 '18 at 23:56
  • 1
    @user7607751 when a function calls itself, it's a recursive function. There's a short overview if you scroll down the [MDN guide on functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions). – Mark Nov 12 '18 at 00:04
  • 1
    Yes, I sure need to read about these concepts, thank you again Mark! – user7607751 Nov 12 '18 at 00:07
1

Mark's answer is probably the most efficient.

But if you are truly looking to loop through nested objects, you might use recursivity :

function loopThroughNewData(newData) {
    if(newData && Object.keys(newData).length > 0) { 
        Object.keys(newData).forEach(function(newKeyLevel) {
            //do stuff
            return loopThroughNewData(newData[newKeyLevel]);
        }
    } else {
        return something;
    }
}
AllirionX
  • 1,073
  • 6
  • 13
  • Yeah, I got to say here the same comment I said to Mark: "This is the first time I came across or clearly notice calling a function from inside itself!" haha. Thank you Al :) I appreciate it! – user7607751 Nov 12 '18 at 00:01