I am struggling to merge 2 objects. Both of them have nested objects and the value of the arbitrary key can be an empty string.
const obj1 = { name: "Arthur", lastName: "King", address: {
street: "",
city: "Some city",
zip: "26772",
}};
const obj2 = { name: "", lastName: "", address: {
street: "Some street",
city: "",
zip: "",
}};
Object.assign
and spread operator are not working as they are doing shallow merge which is not my case, even if I separately merge the nested objects.
The problem is that merging 2 objects which have empty string values doesn't work as expected as Javascript treats empty strings as a value not like undefined
or null
.
const merged = {...obj1, ...obj2, address: {...obj1.address, ...obj2.address}}
The result will be
{
"name": "",
"lastName": "",
"address": {
"street": "Some street",
"city": "",
"zip": ""
}
}
The desired result could be the below one, but somehow it doesn't ignore empty values and treats them like a value.
{
"name": "",
"lastName": "",
"address": {
"street": "Some street",
"city": "Some city",
"zip": "26772"
}
}
One of the workarounds could be removing all the empty string values from the objects and then do merge but IMHO it's a total overkill.
CodePudding user response:
You could greate a new object by taking the entries and check if a value is an object, then take the merged nested objects as value.
This approach works only for same object structures.
const
merge = (a, b) => Object.fromEntries(Object
.entries(a)
.map(([k, v]) => [k, v && typeof v === 'object'
? merge(v, b[k])
: v || b[k]
])
),
obj1 = { name: "Arthur", lastName: "King", address: { street: "", city: "Some city", zip: "26772" } },
obj2 = { name: "", lastName: "", address: { street: "Some street", city: "", zip: "" } },
merged = merge(obj1, obj2);
console.log(merged);
.as-console-wrapper { max-height: 100% !important; top: 0; }
CodePudding user response:
You can recursively merge the items. Note that the snippet below has no support for arrays yet, because you had no arrays. If you want to support arrays as well, let me know.
function merge(main, secondary, deeper) {
let primary = deeper ? structuredClone(main) : main;
for (let sKey in secondary) {
if (!primary[sKey]) primary[sKey] = secondary[sKey];
else {
if (typeof secondary === "object") {
merge(primary[sKey], secondary[sKey], deeper);
} else {
if ([undefined, null, ""].indexOf(primary[sKey]) >= 0) primary[sKey] = secondary[sKey];
}
}
}
return primary;
}
const obj1 = { name: "Arthur", lastName: "King", address: {
street: "",
city: "Some city",
zip: "26772",
}};
const obj2 = { name: "", lastName: "", address: {
street: "Some street",
city: "",
zip: "",
}};
console.log(merge(obj1, obj2));
CodePudding user response:
const obj1 = { name: "Arthur", lastName: "King", address: {
street: "",
city: "Some city",
zip: "26772",
}};
const obj2 = { name: "", lastName: "", address: {
street: "Some street",
city: "",
zip: "",
}};
// ===
function mergeObject (a, b) {
const merged = {};
Object.entries(a).forEach(([key, aValue]) => {
const bValue = b[key];
let mergedValue = null;
if (typeof aValue === 'object') {
mergedValue = mergeObject(aValue, bValue);
}
if (typeof aValue !== 'object') {
const isAValueEmpty = aValue === '' || aValue === undefined || aValue === null;
mergedValue = isAValueEmpty ? bValue : aValue;
}
merged[key] = mergedValue;
});
return merged;
}
console.log(mergeObject(obj1, obj2));
The idea here is that you have a particular type of "is empty" that doesn't match the default. So, we make a custom function to use that condition and then use recursive calls to that function as-needed.
Objects don't have the easiest way to iterate on them with myObject.map()
like arrays, but since you know the structure you expect we can assume that Object.entries(myObject)
will get what you expect.
...
Alternative based on some of the discussion although not sure how readable this is.
The key point is the way the empty status is determined. The use of switch / if / else / ternary I think is up to preference.
const obj1 = { name: "Arthur", lastName: "King", address: {
street: "",
city: "Some city",
zip: "26772",
}};
const obj2 = { name: "", lastName: "", address: {
street: "Some street",
city: "",
zip: "",
}};
// ===
function mergeObject (a, b) {
function isValueEmpty(value) {
return value === ''
|| value === undefined
|| value === null;
}
return Object.fromEntries(Object.entries(a).map(([key, aValue]) => {
const bValue = b[key];
return [
key,
typeof aValue === 'object'
? mergeObject(aValue, bValue)
: (isValueEmpty(aValue)
? bValue
: aValue
)
];
}));
}
console.log(mergeObject(obj1, obj2));