Estoy luchando para fusionar 2 objetos. Ambos tienen objetos anidados y el valor de la clave arbitraria puede ser una cadena vacía.
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
y el operador de distribución no funcionan porque están haciendo una fusión superficial, lo cual no es mi caso, incluso si fusiono por separado los objetos anidados.
El problema es que fusionar 2 objetos que tienen valores de cadena vacíos no funciona como se esperaba, ya que Javascript trata las cadenas vacías como un valor que no es undefined
o null
.
const merged = {...obj1, ...obj2, address: {...obj1.address, ...obj2.address}} The result will be { "name": "", "lastName": "", "address": { "street": "Some street", "city": "", "zip": "" } }
El resultado deseado podría ser el siguiente, pero de alguna manera no ignora los valores vacíos y los trata como un valor.
{ "name": "", "lastName": "", "address": { "street": "Some street", "city": "Some city", "zip": "26772" } }
Una de las soluciones podría ser eliminar todos los valores de cadena vacíos de los objetos y luego fusionarlos, pero en mi humilde opinión, es una exageración total.
Puede agregar un nuevo objeto tomando las entradas y verificando si un valor es un objeto, luego tome los objetos anidados combinados como valor.
Este enfoque funciona solo para las mismas estructuras de objetos.
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; }
Puede fusionar recursivamente los elementos. Tenga en cuenta que el fragmento a continuación aún no es compatible con matrices, porque no tenía matrices. Si también desea admitir matrices, hágamelo saber.
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));
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));
La idea aquí es que tiene un tipo particular de "está vacío" que no coincide con el valor predeterminado. Entonces, creamos una función personalizada para usar esa condición y luego usamos llamadas recursivas a esa función según sea necesario.
Los objetos no tienen la forma más fácil de iterarlos con myObject.map()
como arreglos, pero dado que conoce la estructura que espera, podemos asumir que Object.entries(myObject)
obtendrá lo que espera.
...
Alternativa basada en parte de la discusión, aunque no estoy seguro de cuán legible es.
El punto clave es la forma en que se determina el estado vacío. El uso de switch/if/else/ternary creo que depende de la preferencia.
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));