Tengo dos objetos con la siguiente estructura:
let sourceObj = [ { items: [ { items: [{ id: '0', name: 'z' }], name: 'm' }, { items: [{ id: '2', name: 'q' }], name: 'l' }, ], name: 'c' }, { items: [ { items: [{ id: '4', name: '-' }], name: 'v' }, ], name: 'd' } ]; let targetObj = [ { items: [ { items: [{ id: '1', name: 'd' }], name: 'm' }, { items: [{ id: '3', name: 'b' }], name: 'j' }, ], name: 'c' } ];Quiero fusionar este objeto para obtener un objeto con la siguiente estructura:
let merged = [ { items: [ { items: [ { id: '0', name: 'd' }, { id: '1', name: 'z' }], name: 'm' }, { items: [{ id: '2', name: 'q' }], name: 'l' }, { items: [{ id: '3', name: 'b' }], name: 'j' }, ], name: 'c' }, { items: [ { items: [{ id: '4', name: '-' }], name: 'v' }, ], name: 'd' } ]Es decir, quiero obtener el objeto, que se ha unido a las matrices si el nombre de la matriz de origen es el mismo en la matriz de destino.
Intenté usar el método lodash mergeWith, pero solo pude unir elementos de nivel superior de colecciones ...
mergeWith(sourceObj, targetObj, (objValue, srcValue) => { if (isArray(objValue)) { return objValue.concat(srcValue); } });Tiene razón en que el mergeWith de mergeWith no se fusiona recursivamente. Puede administrar esto usted mismo manualmente.
name .name coincide, y si es así, llame recursivamente a la función de merge para combinar las dos matrices anidadas de las matrices de origen y destino, de lo contrario, agregue el elemento de la segunda matriz al mapa de objetos.Código:
const merge = (sourceArr, targetArr) => { // (1) generate lookup map const objMap = sourceArr.reduce((map, curr) => { return { ...map, [curr.name]: curr }; }, {}); // (2) Merge objects, recurse on items arrays targetArr.forEach((obj) => { if (objMap[obj.name]) { objMap[obj.name].items = merge(objMap[obj.name].items, obj.items); } else { objMap[obj.name] = obj; } }); // (3) Return merged values array return Object.values(objMap); }; const sourceObj = [ { items: [ { items: [{ id: "0", name: "z" }], name: "m" }, { items: [{ id: "2", name: "q" }], name: "l" } ], name: "c" }, { items: [{ items: [{ id: "4", name: "-" }], name: "v" }], name: "d" } ]; const targetObj = [ { items: [ { items: [{ id: "1", name: "d" }], name: "m" }, { items: [{ id: "3", name: "b" }], name: "j" } ], name: "c" } ]; const merge = (sourceArr, targetArr) => { const objMap = sourceArr.reduce((map, curr) => { return { ...map, [curr.name]: curr }; }, {}); targetArr.forEach((obj) => { if (objMap[obj.name]) { objMap[obj.name].items = merge(objMap[obj.name].items, obj.items); } else { objMap[obj.name] = obj; } }); return Object.values(objMap); }; const res = merge(sourceObj, targetObj); console.log(res);Encuentro que una dosis de recursividad mutua hace que el código sea más limpio aquí.
const combineItems = (o = {}, items) => o .items || items .length ? {items: deepMerge (o.items || [], items)} : {} const deepMerge = (xs, ys) => Object .values ([... xs, ... ys] .reduce ( (a, {name, items = [], ...rest}) => ({ ... a, [name]: { ... (a [name] || {name, ...rest}), ... combineItems (a [name], items) } }), {} )) const sourceObj = [{items: [{items: [{id: "0", name: "z"}], name: "m"}, {items: [{id: "2", name: "q"}], name: "l"}], name: "c"}, {items: [{items: [{id: "4", name: "-"}], name: "v"}], name: "d"}] const targetObj = [{items: [{items: [{id: "1", name: "d"}], name: "m"}, {items: [{id: "3", name: "b"}], name: "j"}], name: "c"}]; console .log (deepMerge (sourceObj, targetObj)) .as-console-wrapper {max-height: 100% !important; top: 0} deepMerge es la función principal, pero delega a combineItems para manejar las diversas combinaciones de si ya tenemos elementos para combinar o no. combineItems devolverá algo como {items: [<item1>. <item2>, ...]} o simplemente {} , dependiendo de si se han encontrado elementos.
Hay un problema de rendimiento potencial aquí, que Rich Snapp ha denominado The reduce ({...spread}) anti-pattern . Personalmente, no me preocuparía aquí si el código funciona a su satisfacción. Pero si no, podemos cambiarlo para que coincida con sus sugerencias, a algo como esto:
const deepMerge = (xs, ys) => Object .values ([... xs, ... ys] .reduce ( (a, {name, items = [], ...rest}) => { const x = a [name] || {name, ...rest} if (items.length || x.items) x .items = deepMerge (x .items || [], items) a [name] = x return a }, {} ))