Digamos que tengo una serie de objetos como este:
[ { "transactiondate.Display.V1.FormattedValue": "2/1/2016", "transactiondate": "2016-02-01T08:00:00Z", "level": "level 1", "amount": "5" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2017", "transactiondate": "2017-02-01T08:00:00Z", "level": "level 1", "amount": "7" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2017", "transactiondate": "2017-02-01T08:00:00Z", "level": "level 1", "amount": "4" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2016", "transactiondate": "2016-02-01T08:00:00Z", "level": "level 2", "amount": "10" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2016", "transactiondate": "2016-02-01T08:00:00Z", "level": "level 2", "amount": "20" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2017", "transactiondate": "2017-02-01T08:00:00Z", "level": "level 2", "amount": "50" }, ... ]
Quiero que la salida se vea así:
[ { name: "level 1", data: [5, 11] // Total amount in order of ascending year. 2016 had total amount of 5 and 2017 had total amount of 11 }, { name: "level 2", data: [30, 50] }, { name: "level x", data: [...] } ]
Pude agruparlo con éxito por año haciendo lo siguiente, pero no estoy muy seguro de cómo tomar el objeto resultante y luego transformarlo en el resultado deseado. Tendría que recorrer las entradas del objeto, dividirlo en grupos por "nivel", iterar a través de las entradas nuevamente, luego usar reduce para acumular las cantidades, luego dividirlas nuevamente en clave/valor "nombre" y "datos" pares? Siento que eso es muy ineficiente si puedo hacer que funcione. Cualquier ayuda sería muy apreciada.
var groupedByYr = data.reduce(function (r, a) { r[a.transactiondate.substring(0,4)] = r[a.transactiondate.substring(0,4)] || []; r[a.transactiondate.substring(0,4)].push(a); return r; });
tal vez no sea la respuesta perfecta, pero tiene un bucle 1, supongo: D
Traté de explicarlo en el código tanto como pude
var data = [{ "transactiondate.Display.V1.FormattedValue": "2/1/2016", "transactiondate": "2016-02-01T08:00:00Z", "level": "level 1", "amount": "5" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2017", "transactiondate": "2017-02-01T08:00:00Z", "level": "level 1", "amount": "7" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2017", "transactiondate": "2017-02-01T08:00:00Z", "level": "level 1", "amount": "4" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2016", "transactiondate": "2016-02-01T08:00:00Z", "level": "level 2", "amount": "10" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2016", "transactiondate": "2016-02-01T08:00:00Z", "level": "level 2", "amount": "20" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2017", "transactiondate": "2017-02-01T08:00:00Z", "level": "level 2", "amount": "50" }, ], parse = [] //save parse array, levels = {} //save level keys; data.map((item) => { let map = new Map(); // create a map for location of the value of the each year if (typeof levels[item.level] == 'undefined') { // we didn't parse the level yet so it's new map.set(item.transactiondate.substring(0, 4), 0); // set the location of the amount of the first year as 0 because it's the first let key = parse.push({ name: item.level, yearindexes: map, data: [parseInt(item.amount)] }); // save the parse array size as level key levels[item.level] = key - 1; // save the level and index of it (key -1) } else { // parse the level before so it's exists if (parse[levels[item.level]].yearindexes.has(item.transactiondate.substring(0, 4))) { // we have the year in the data let yearindex = parse[levels[item.level]].yearindexes.get(item.transactiondate.substring(0, 4)); // get the index of the year parse[levels[item.level]].data[yearindex] += parseInt(item.amount); // add the amount of the year to the index } else { // it's a new years parse[levels[item.level]].data.push(parseInt(item.amount));// push the new year amount to the data map = parse[levels[item.level]].yearindexes; // get previes year indexes map.set(item.transactiondate.substring(0, 4), map.size); // add the new year with the size of yearindexes as the index of the new year parse[levels[item.level]].yearindexes = map; // set the new yearindexes } } }); //remove yearindexes from parse (optional) const result = parse.map(({yearindexes,...rest}) => ({...rest})); console.log(result);
Creo que solo tendrá que hacerlo en dos pases, uno con forEach para colocar todo en un búfer y otro con map para tomar ese búfer y formatearlo como desee. Creo que puedes hacerlo de una vez, pero será mucho más fácil de leer si no lo haces.
Aquí uso forEach para recorrer los elementos de datos y uso un buffer
objetos. Dado que es un objeto, puedo usar claves para mantener las entradas únicas y seguir agregando las cantidades para cada año usando el año como una subclave (que obtengo de getYear() de Date). La primera vez que presione cada tecla, deberá verificar su existencia en el objeto de destino O puede hacer lo que hice yo, la fusión nula a un valor predeterminado (ya sea un objeto vacío o 0).
Luego, cuando se crea el búfer, uso Object.values para asignar el objeto a una matriz y también para convertir las cantidades de objetos en matrices.
Déjame saber si lo hice bien.
const data = [ { "transactiondate.Display.V1.FormattedValue": "2/1/2016", "transactiondate": "2016-02-01T08:00:00Z", "level": "level 1", "amount": "5" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2017", "transactiondate": "2017-02-01T08:00:00Z", "level": "level 1", "amount": "7" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2017", "transactiondate": "2017-02-01T08:00:00Z", "level": "level 1", "amount": "4" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2016", "transactiondate": "2016-02-01T08:00:00Z", "level": "level 2", "amount": "10" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2016", "transactiondate": "2016-02-01T08:00:00Z", "level": "level 2", "amount": "20" }, { "transactiondate.Display.V1.FormattedValue": "2/1/2017", "transactiondate": "2017-02-01T08:00:00Z", "level": "level 2", "amount": "50" } ]; let buffer = {}; data.forEach(i=>{ let t = new Date(i.transactiondate).getYear(); let amts = buffer[i.level]?.amts || {}; amts[t] = i.amount*1 + (amts[t] || 0); buffer[i.level] = {name: i.level, amts: amts}; }); //console.log(buffer); let final = Object.values(buffer).map(m=>{ return {name: m.name, data: Object.values(m.amts)}; }); console.log(final);