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);
假设我有一个这样的对象数组:
[
{
"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);