Reputation: 3484
EDIT: Thanks 4 all the great, diverse answers - I chose the solution that worked for me even after I realized that I needed more requirements: I also needed new properties to be added and for it to work with arrays in objects as well.
Here's what I wanna do: Update one object through another one.
Here are some constraints:
My Problem is that I don't know how to easily do this for nested objects because typeof x === "object"
also returns true for Date objects for example.
Here's what I got so far:
let originalObj = {
dateCreated: new Date(2021, 1, 10),
value: "100",
subObj: {
count: 55,
val: null
}
};
let updateObj = {
dateCreated: new Date(2021, 1, 11),
subObj: {
val: 90
}
};
let updateOrignal = (oObj, uObj) => {
for (let prop in uObj) {
if (uObj.hasOwnProperty(prop) &&
oObj.hasOwnProperty(prop)) {
oObj[prop] = uObj[prop];
}
}
};
console.log(originalObj);
updateOrignal(originalObj, updateObj)
console.log(originalObj);
Currently my updated object looks like this:
{
"dateCreated": "2021-02-10T23:00:00.000Z",
"value": "100",
"subObj": {
"val": 90
}
}
My goal:
{
"dateCreated": "2021-02-10T23:00:00.000Z",
"value": "100",
"subObj": {
"count": 55,
"val": 90
}
}
Upvotes: 5
Views: 2365
Reputation: 122057
You could use recursive approach with reduce
method and for..in
loop and also check if the type of object is Date
. This solution will not work with arrays and also will not modify original data.
let originalObj = {
dateCreated: new Date(2021, 1, 10),
value: "100",
subObj: {
count: 55,
val: null
}
};
let updateObj = {
dateCreated: new Date(2021, 1, 11),
subObj: {
val: 90
}
};
function update(o1, o2) {
return Object.entries(o1).reduce((r, e) => {
for (let p in o2) {
if ([o1[p], o2[p]].every(o => typeof o === 'object')) {
r[p] = o2[p] instanceof Date ? o2[p] : update(r[p], o2[p])
} else {
r[p] = o2[p]
}
}
return r
}, { ...o1 })
}
const result = update(originalObj, updateObj)
console.log(result)
Upvotes: 2
Reputation: 530
This is what worked for me in the past. You can clean it up a little and customize for your purposes but the idea is as follows:
let originalObj = {
dateCreated: new Date(2021, 1, 10),
value: "100",
subObj: {
count: 55,
val: null
}
};
let updateObj = {
dateCreated: new Date(2021, 1, 11),
subObj: {
val: 90
}
};
function mergeDeep(target, source) {
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) {
Object.assign(target, { [key]: {} });
}
mergeDeep(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
}
function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
console.log(originalObj);
mergeDeep(originalObj, updateObj);
console.log(originalObj);
Upvotes: 1
Reputation: 2089
This is the solution you need (from https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6), actually you can search on google "deep merge" or "recursively merge two javascript objects"
I added ES6 sintax {...obj} to be sure to clone objects before merging them
let originalObj = {
dateCreated: new Date(2021, 1, 10),
value: "100",
subObj: {
count: 55,
val: null
}
};
let updateObj = {
dateCreated: new Date(2021, 1, 11),
subObj: {
val: 90
}
};
const merge = (target, source) => {
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (const key of Object.keys(source)) {
if (source[key] instanceof Object) Object.assign(source[key], merge(target[key], source[key]))
}
// Join `target` and modified `source`
Object.assign(target || {}, source)
return target
}
console.log(merge({ ...originalObj}, {...updateObj}));
Upvotes: 4
Reputation: 31815
You could test the constructor of the values like this:
let originalObj = {
dateCreated: new Date(2021, 1, 10),
value: "100",
subObj: {
count: 55,
val: null
}
};
let updateObj = {
dateCreated: new Date(2021, 1, 11),
subObj: {
val: 90
}
};
let updateOriginal = (original, patch) => {
Object.entries(patch).forEach(([key, value]) => {
value && value.constructor === Object && patch[key]
? updateOriginal(original[key], patch[key])
: (original[key] = patch[key]);
});
}
updateOriginal(originalObj, updateObj);
console.log(originalObj);
Upvotes: 2