Tengo el siguiente objeto:
var series = [{ name: 'Series 1', data: [1400, 999, 450], tag: "Tag 1" }, { name: 'Series 2', data: [355, 188, 99], tag: "Tag 1" }, { name: 'Series 3', data: [205, 488, 104], tag: "Tag 2" }];Lo que estoy tratando de hacer es realizar una función groupBy para esa matriz, por ejemplo, llamar a orderBy ("etiqueta") debería devolver el siguiente resultado:
[{ name: "Series 1 / Series 2", data: [1755, 1187, 549], // => Sum: Series1[0] + Series2[0], Series1[1] + Series2[1], etc... tag: "Tag 1" }, { name "Series 3", data: [205, 488, 104], tag: "Tag 2" }]De momento esto es lo que tengo hasta ahora:
var seriesArray = [{ name: 'Series 1', data: [1400, 999, 450], tag: "Tag 1" }, { name: 'Series 2', data: [355, 188, 99], tag: "Tag 1" }, { name: 'Series 3', data: [205, 488, 104], tag: "Tag 2" }]; const groupBy = (key) => seriesArray.reduce((total, currentValue) => { const newTotal = total; if ( total.length && total[total.length - 1][key] === currentValue[key] ) newTotal[total.length - 1] = { ...total[total.length - 1], ...currentValue, data: parseInt(total[total.length - 1].data[0]) + parseInt(currentValue.data[0]), }; else newTotal[total.length] = currentValue; return newTotal; }, []); console.log(groupBy('tag'));Como puede ver, parece funcionar orderBy ("etiqueta") pero estoy haciendo la operación de suma explícita, el principal problema con esto es que los datos [] podrían tener X elementos.
Actualmente, su enfoque asume que el último elemento que se agregó a su matriz total tiene la misma key la que está tratando de agrupar. Todo esto está bien si los datos están ordenados por key (es decir, todas las "Etiquetas 1" vienen primero), pero puede ser un problema si no lo está. Sugeriría que, en cambio, primero cree un objeto/ Map que esté marcado por tag . De esa manera, puede usar el objeto para encontrar sus objetos "Tag 1" , "Tag 2" , etc. acumulados. Una vez que lo haya reducido a un objeto/mapa, puede tomar los valores (que son los objetos acumulados) y colocarlos en una matriz. Para manejar las diferentes longitudes de la matriz data , puede usar .map() en su matriz de datos acumulada (es decir, seen.data ) y luego agregar cada número con el número correspondiente de la matriz de data del objeto actual usando el argumento de índice i :
const seriesArray = [{ name: 'Series 1', data: [1400, 999, 450], tag: "Tag 1" }, { name: 'Series 2', data: [355, 188, 99], tag: "Tag 1" }, { name: 'Series 3', data: [205, 488, 104], tag: "Tag 2" }]; const groupBy = (arr, key) => Array.from(arr.reduce((map, currentObj) => { const groupOnKey = currentObj[key]; const seen = map.get(groupOnKey); return map.set(groupOnKey, seen ? { ...seen, name: seen.name + " / " + currentObj.name, data: seen.data.map((num, i) => num + currentObj.data[i], 0) } : currentObj); }, new Map()).values()); console.log(groupBy(seriesArray, 'tag'));Buen reto, esto se me ocurre con findIndex
const groupBy = (key) => seriesArray.reduce((total, currentValue) => { index = total.findIndex(x => x[key] === currentValue[key]) if (index >= 0) { total[index].data = total[index].data.map(function (num, idx) { return num + parseInt(currentValue.data[idx]); }); total[index].name += " / " + currentValue.name; } else { total.push(currentValue); } return total; }, []);Creo que la lógica en su función de devolución de llamada reducida no le dará el resultado que desea. Voy a explicar lo que está haciendo esa función.
Que quieres hacer
var seriesArray = [{ name: 'Series 1', data: [1400, 999, 450], tag: "Tag 1" }, { name: 'Series 3', data: [1400, 999, 450], tag: "Tag 2" }, { name: 'Series 2', data: [355, 188, 99], tag: "Tag 1" }, ]; const isMatchingArray = (array1, array2) => { if (array1.length !== array2.length) { return false; } for (let i = 0; i < array1.length; i++) { if (array1[i] !== array2[i]) { return false; } } return true; } const groupBy = (key, array) => array.reduce((total, currentValue) => { const newTotal = total; if (total.length) { const matchingItemIndex = total.findIndex(element => { if (typeof element[key] === 'string') { return element[key] === currentValue[key]; } else if (Array.isArray(element[key])) { return isMatchingArray(element[key], currentValue[key]); } }); if (matchingItemIndex !== -1) { newTotal[matchingItemIndex] = { ...total[matchingItemIndex], ...currentValue, name: total[matchingItemIndex].name + ' / ' + currentValue.name, data: currentValue.data.map((dataItem, index) => parseInt(dataItem + total[matchingItemIndex].data[index])), }; } else { newTotal.push(currentValue) } } else { newTotal.push(currentValue) }; return newTotal; }, []); console.log(groupBy('tag', seriesArray)) console.log(groupBy('data', seriesArray));