supersan supersan - 2 months ago 7
Javascript Question

How to add / replace values in a nested object (without losing original references) in Javascript?

I have to update a nested object with the new data without losing references in the original object. I think my problem is best explained with an example:

Original Object:

{
"one": 1,
"two": {
"three": 3,
"four": {
"five": 5
}
}
}


Update object:

{
"two": {
"three": 5,
"four": {
"five": 7,
"new": 8
}
}
}


Final object: (while retaining references in original object)

{
"two": {
"three": 5,
"four": {
"five": 7,
"new": 8
}
}
}


So while it may look like the Update object and Final object are exactly the same, the thing to note is that I have to keep the references from the original object (i.e. I don't replace the "two" object or the "four" object inside the "two" object, only update their values).

The reason for preserving references is that because I'm using the data structure to create various bindings in AngularJS. I've tried using
angular.copy
but it does not give me the desired effect. Also if there is a pure JS implementation for this, I'd love to see it so that I can learn from the code.

What is the most efficient way to accomplish this?

Answer

Maybe this works for you. First the unwanted keys are deleted and then the values updated.

var original = { "one": 1, "two": { "three": 3, "four": { "five": 5 } } },
    update = { "two": { "three": 5, "four": { "five": 7, "new": 8 } } };

function deep(o, u) {
    var keysO = Object.keys(o),
        keysU = Object.keys(u);

    keysO.forEach(function (k) {
        if (-1 === keysU.indexOf(k)) {
            delete o[k];
        }
    });
    keysU.forEach(function (k) {
        if (typeof u[k] === 'object') {
            if (u[k] !== null && typeof o[k] !== 'object') {
                o[k] = Array.isArray(u[k]) ? [] : {};
            }
            deep(o[k], u[k]);
            return;
        }
        o[k] = u[k];
    });
}

deep(original, update);

document.write('<pre>' + JSON.stringify(original, 0, 4) + '</pre>');