Home > Net >  JS - How to transform an array of objects into different groups based off property value, further se
JS - How to transform an array of objects into different groups based off property value, further se

Time:11-19

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;
            });

CodePudding user response:

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);
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

I think you're just going to have to do it in two passes, one with reduce 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 reduce to loop over the data elements, and I pass buffer to reduce as an argument, so that I can just keep building the buffer object. Since it's an object, I can keep adding the amounts for each year using the year as a key (which I get from Date's getYear()), and then when I'm done I use Object.values to map the object to an array.

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.reduce((d, 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};
}, buffer);

//console.log(buffer);
let final = Object.values(buffer).map(m=>{
  return {name: m.name, data: Object.values(m.amts)};
});

console.log(final);
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related