Let' say I have an array of objects like this:
[
{
"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"
},
...
]
I want the output to look like this:
[
{
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: [...]
}
]
I was able to successfully group it by year by doing the following but I'm not quite sure how to go about taking the resulting object then transforming it into the desired output. I would have to loop through entries of the object, split it up into groups by "level", iterate through the entries again then use reduces to accumulate the amounts, then split them up again into "name" and "data" key/value pairs? I feel like that is very inefficient if I can even get that to work. Any help would be much appreciated.
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;
});
maybe not the perfect answer but it has a 1 loop I guess :D
I tried to explain it in the code as much as I could
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);
I think you're just going to have to do it in two passes, one with forEach to get everything into a buffer, and one with map to take that buffer and format it how you like. I think you can do it in one pass, but it is going to be a lot easier to read if you don't.
Here I use forEach to loop over the data elements, and I use an object buffer
. Since it's an object, I can use keys to keep the entries unique, and keep adding the amounts for each year using the year as a subkey (which I get from Date's getYear()). The first time you hit each key, you will either need to check for its existence in the target object OR you can do what I did, null coalesce to a default value (either an empty object or 0).
Then when the buffer is built, I use Object.values to map the object to an array and also to convert the amounts objects to arrays.
Let me know if I got that right.
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);