1

I asked this question before but I am facing a different problem now.

I have these objects:

let oldObj = { 
    test1: 'valueTest1',
    test2: {
        inner1: 'valueInner1',
        inner2: 'valueInner2',
        inner3: {
            otherInner1: 'valueOtherInner1',          
            otherInner2: 'valueOtherInner2'
            }
        },
    test3: 'valueTest3' 
};

let newObj = {
    test2: {
        inner1: 'newValueInner1',
        }
}

and using this code:

for (const key of Object.keys(newObj)) {
  if (key in oldObj) {
    oldObj[key] = newObj[key];
  }
}

I get:

{
    test1:'valueTest1',
    test2: {
        inner1: 'newValueInner1'
        },
    test3:'valueTest3'
}

But I don't want to remove the other properties of the inner objects (inner2, inner3, etc). I would expect this result:

{ 
    test1: 'valueTest1',
    test2: {
        inner1: 'newValueInner1',
        inner2: 'valueInner2',
        inner3: {
            otherInner1: 'valueOtherInner1',          
            otherInner2: 'valueOtherInner2'
            }
        },
    test3: 'valueTest3' 
}

Is there a way of having a function that does this?

The objects here are just examples, what I want to achieve is to create a function that deep modifies object properties without removing the rest.

For example, if I define newObj as:

let newObj = {
    test2: {
        inner3: {
            otherInner1:'newValueInner1'
            }
        }
}

I should get:

{ 
    test1: 'valueTest1',
    test2: {
        inner1: 'valueInner1',
        inner2: 'valueInner2',
        inner3: {
            otherInner1: 'newValueInner1',          
            otherInner2: 'valueOtherInner2'
            }
        },
    test3: 'valueTest3' 
};
c-chavez
  • 7,237
  • 5
  • 35
  • 49

3 Answers3

1

If you're able to use destructuring, you can destructure the value from newObj onto oldObj

let oldObj = { 
    test1: 'valueTest1',
    test2: {
        inner1: 'valueInner1',
        inner2: 'valueInner2',
        inner3: {
            otherInner1: 'valueOtherInner1',          
            otherInner2: 'valueOtherInner2'
            }
        },
    test3: 'valueTest3' 
};

let newObj = {
    test2: {
        inner1: 'newValueInner1',
    }
}

for (const key of Object.keys(newObj)) {
  if (key in oldObj) {
    oldObj[key] = {
      ...oldObj[key], // set oldObj[key] to itself
      ...newObj[key]  // override properties from newObj[key]
    }
  }
}

console.log(oldObj)

Edit: As @riv pointed out, you can also use Object.assign:

oldObj[key] = Object.assign(oldObj[key], newObj[key]);
chazsolo
  • 7,873
  • 1
  • 20
  • 44
  • 1
    If destructuring isn't available, `Object.assign` is an equivalent alternative. – riv Aug 29 '18 at 20:14
  • There's a slight difference here though - your destructuring example clones oldObj, while `assign` updates its first argument. If someone was referencing it elsewhere, they would keep the old values in the first case. And in both cases those are shallow updates, which is not necessarily what the author wants. – riv Aug 29 '18 at 20:24
  • 1
    Problem here is if I use an object that changes inner3.otherInner1, the value for inner3.otherInner2 gets removed, which I must avoid. – c-chavez Aug 29 '18 at 23:15
1

You need to iterate the objects entries and if another object is found, take the nested object for updating.

function update(target, source) {
    Object.entries(source).forEach(([key, value]) => {
        if (value && typeof value === 'object') {
            update(target[key] = target[key] || {}, value);
            return;
        }
        target[key] = value;
    });
}

var oldObj = { test1: 'valueTest1', test2: { inner1: 'valueInner1', inner2: 'valueInner2', inner3: { otherInner1: 'valueOtherInner1', otherInner2: 'valueOtherInner2' } }, test3: 'valueTest3' },
    newObj = { test2: { inner1: '!!!!!!!!!!!!' } };

update(oldObj, newObj);
console.log(oldObj);
Fabian N.
  • 3,807
  • 2
  • 23
  • 46
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

What the function is doing is it is looping through the first level I.E. test1, test2, test3. Then it takes the new object and instead of inserting the new values it replaces the new value. What you need to do is put in an inner loop inside the inner object then update the values. Like this:

for (var item in newObj)
{
  // is item an value item?
  if(typeof newObj[item] == "string")
  {
     oldObj[item] = newObj[item];
  }
  // Is the value an object with inner items?
  else if(typeof newObj[item] == "object")
  {
    for(var innerItem in newObj[item])
    {
      if(typeof newObj[item][innerItem] == "string")
      {
         oldObj[item][innerItem] = newObj[item][innerItem];
      }
    }
  }
}
943A
  • 166
  • 8