JS - 如何根据 属性 值将对象数组转换为不同的组,进一步分离数据,并聚合一些数据

JS - How to transform an array of objects into different groups based off property value, further segregate data, and aggregating some of the 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"
    },
    ...
]

我希望输出如下所示:

[
    {
        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: [...]
    }
]

通过执行以下操作,我能够按年成功地对其进行分组,但我不太确定如何获取生成的对象,然后将其转换为所需的输出。我将不得不遍历对象的条目,按“级别”将其分成组,再次遍历条目然后使用 reduce 来累积数量,然后再次将它们分成“名称”和“数据”key/value 对?如果我什至可以让它工作,我觉得那是非常低效的。任何帮助将不胜感激。

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

也许不是完美的答案,但我猜它有一个循环 :D

我尽量在代码中解释它

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

我认为您只需分两步完成,一次使用 forEach 将所有内容放入缓冲区,另一次使用 map 获取该缓冲区并按照您喜欢的方式对其进行格式化。我认为你可以一次完成,但如果你不这样做,它会更容易阅读。

这里我使用forEach 来遍历数据元素,并且我使用了一个对象buffer。因为它是一个对象,所以我可以使用键来保持条目的唯一性,并使用年份作为子键(我从 Date 的 getYear() 获得)继续添加每年的金额。第一次按下每个键时,您需要检查它是否存在于目标对象中,或者您可以像我一样,将 null 合并为默认值(空对象或 0)。

然后在构建缓冲区时,我使用 Object.values 将对象映射到数组,并将金额对象转换为数组。

如果我做对了请告诉我。

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