Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically set property of nested object if they exist. Recreate _.extend

Tags:

I am looking to recreate the following as seen below, dynamically without having manually define where the matches are in the Object's properties.

Desired Outcome

const obj = {
  levelOne: {
    someFun: () => {},
    levelTwo: {
      anArray: [],
      something: 'asas',
      levelThree: {
        stay: 'the same',
        name: 'Set this one!',
      },
    },
  },
}

const updatedObj = {
  ...obj,
  levelOne: {
    ...obj.levelOne,
    levelTwo: {
      anArray: [],
      ...obj.levelOne.levelTwo,
      levelThree: {
        ...obj.levelOne.levelTwo.levelThree,
        name: 'Updated prop!',
      },
    },
  },
}

console.info(updatedObj)
{
 levelOne: {
   someFun: () => {},
   levelTwo: {
     something: 'asas',
       levelThree: {
         stay: 'the same',
         name: "Updated prop!",
       },
     },
   },
 }

So far

const inputOriginal = {
  levelOne: {
    levelTwo: {
        something: 'asas',
        levelThree: {
        name: "Original input!"
      }
    }
  }
}
const newInput = {
  levelOne: {
    levelTwo: {
      levelThree: {
        name: "Updated prop!"
      }
    }
  }
}

const mergeObjects = function(overWrite, original){
    return Object.entries(original).reduce( (acc, [ key, item ]) => {
        if(typeof item === 'object' && overWrite[key]) {
            mergeObjects(overWrite[key], item)
            acc = {
                ...acc,
                [key]: item
            }
        } else if(overWrite[key]) {
            acc = {
                ...acc,
                [key]: overWrite[key]
            }
        }
        return acc
    }, {})
}
const merged = mergeObjects(inputOriginal,newInput) //merged.levelOne.levelTwo.levelThree = "Updated prop!"

However I can see there is an error logic in my code that, when it comes back out of the recursion, it overwrites the changes values with its self, which has the original values.

How can I create a function that will do the same as 'Desired Outcome'?

like image 286
Jamie Hutber Avatar asked Dec 20 '19 14:12

Jamie Hutber


People also ask

How do you change the properties of a nested object?

To update nested properties in a state object in React: Pass a function to setState to get access to the current state object. Use the spread syntax (...) to create a shallow copy of the object and the nested properties. Override the properties you need to update.

What is a nested object?

Nested objects are the objects that are inside an another object. In the following example 'vehicles' is a object which is inside a main object called 'person'. Using dot notation the nested objects' property(car) is accessed.

How do you make a nested object in JavaScript?

const obj = { code: "AA", sub: { code: "BB", sub: { code: "CC", sub: { code: "DD", sub: { code: "EE", sub: {} } } } } }; Notice that for each unique couple in the string we have a new sub object and the code property at any level represents a specific couple. We can solve this problem using a recursive approach.


1 Answers

You can try with this function :

const mergeObjects = (obj, updates) => {
  for (const key of Object.keys(updates)) {
    if (updates[key] instanceof Object) Object.assign(updates[key], mergeObjects(obj[key], updates[key]))
  }
  Object.assign(obj || {}, updates)
  return obj
}

Here is a working example using the given objects :

const inputOriginal = {
  levelOne: {
    levelTwo: {
      something: 'asas',
      levelThree: {
        name: "Original input!"
      }
    }
  }
}
const newInput = {
  levelOne: {
    levelTwo: {
      levelThree: {
        name: "Updated prop!"
      }
    }
  }
}

const mergeObjects = (obj, updates) => {
  for (const key of Object.keys(updates)) {
    if (updates[key] instanceof Object) Object.assign(updates[key], mergeObjects(obj[key], updates[key]))
  }
  Object.assign(obj || {}, updates)
  return obj
}

const merged = mergeObjects(inputOriginal, newInput)
console.log(merged);
like image 96
Raphael Deiana Avatar answered Sep 30 '22 19:09

Raphael Deiana