使用动态键合并和求和对象数组
Posted
技术标签:
【中文标题】使用动态键合并和求和对象数组【英文标题】:Merge and sum array of objects with dynamic keys 【发布时间】:2021-12-27 01:26:35 【问题描述】:我有一个对象数组的数据结构,其中花费了许多不同角色的小时数,如下所示:
["week 01" : "Developer" : 1,
"week 01" : "Project Manager" : 5,
"week 01" : "Project Manager" : 6,
"week 01": "Developer" : 8,
"week 02" : "Strategy" : 4]
角色和周是动态的(即我事先不知道键/值),我想将同一角色和同一周的所有时间相加以获得以下输出:
["week 01" :
"Developer" : 9
"Project Manager" : 11
,
"week 02" :
"Strategy" : 4
]
我曾尝试使用reduce
函数,但似乎无法正确使用:
function reduceArray(arr)
const result = arr.reduce((result, item) =>
let obj = ;
const existing = result.find(function (x)
for (var prop in x)
if (x[prop] === item[prop])
//same week number found
//loop through roles
for (var subProp in x[prop])
if (x[prop][subProp] === item[prop][subProp])
obj[prop][subProp] += x[prop][subProp] + item[prop][subProp]
//This is where I lose myself in the prop-loops....
)
, [])
return result;
【问题讨论】:
你真的需要每个星期的数组中的单个对象吗?为什么不只使用一个对象? 我在一个非结构化的 Excel 表中循环,所有的日子都是单独的行,所以每个条目都是一个独立的单元格,这就是为什么它们最终成为单个对象 但是是的,最终结果绝对可能看起来像 "week 01" : role: hours, "week 02" : role : hours 【参考方案1】:如果每个条目只有一个(参见第二个[0]
)角色,您可以使用以下内容:
const summary = work.reduce((acc,cur) =>
let [week, val] = Object.entries(cur)[0];
let [role, hours] = Object.entries(val)[0];
acc[week] = acc[week] || ;
acc[week][role] = (acc[week][role] || 0) + hours;
return acc;
,);
/* OUTPUT:
"week 01":
"Developer": 9,
"Project Manager": 11
,
"week 02":
"Strategy": 4
*/
演示
const work = ["week 01" : "Developer" : 1,
"week 01" : "Project Manager" : 5,
"week 01" : "Project Manager" : 6,
"week 01": "Developer" : 8,
"week 02" : "Strategy" : 4]
const summary = work.reduce((acc,cur) =>
let [week, val] = Object.entries(cur)[0];
let [role, hours] = Object.entries(val)[0];
acc[week] = acc[week] || ;
acc[week][role] = (acc[week][role] || 0) + hours;
return acc;
,);
console.log(summary);
//To convert summary into an array of objects
const arrsum = Object.entries(summary)
.map(entry => Object.fromEntries([entry]));
console.log( arrsum );
要将输出转换为对象数组,请使用以下代码:
const arrsum = Object.entries(summary)
.map(entry => Object.fromEntries([entry]));
【讨论】:
【参考方案2】:按周数分组,然后按作业名称对每周数分组并求和。
const arr = [
"week 01": Developer: 1 ,
"week 01": "Project Manager": 5 ,
"week 01": "Project Manager": 6 ,
"week 01": Developer: 8 ,
"week 02": Strategy: 4 ,
];
// First group By weeks num:
function groupByWeekNum(weeksArray)
return weeksArray.reduce((weeks, currWeek) =>
const weekNum = Object.entries(currWeek)[0][0];
weeks[weekNum] ??= [];
weeks[weekNum].push(currWeek);
return weeks;
, );
结果如下:
"week 01": [
"week 01":
"Developer": 1
,
"week 01":
"Project Manager": 5
,
"week 01":
"Project Manager": 6
,
"week 01":
"Developer": 8
],
"week 02": [
"week 02":
"Strategy": 4
]
然后是每一周的分组,按Job分组,累加每个job.Developer的值
function groupByWeekJob(jobsArr)
const grouping = jobsArr.reduce((jobs, currJob) =>
const jobName = Object.entries(currJob)[0][0];
const jobValue = Object.entries(currJob)[0][1];
jobs[jobName] ??= 0;
if (jobName in jobs)
jobs[jobName] += jobValue;
else
jobs[jobName] = jobValue;
return jobs;
, );
return grouping;
调用和调用函数:
const res = Object.entries(groupByWeekNum(arr)).map(([week, weekJobs]) =>
groupByWeekJob(weekJobs.map((week) => Object.entries(week)[0][1]))
);
结果 console.log(res, null, 4);
:
[
"Developer": 9,
"Project Manager": 11
,
"Strategy": 4
]
【讨论】:
【参考方案3】:这是一个简单的方法。希望 cmets 有用。
const data = [
"week 01": Developer: 1 ,
"week 01": "Project Manager": 5 ,
"week 01": "Project Manager": 6 ,
"week 01": Developer: 8 ,
"week 02": Strategy: 4
];
function cleanUp(data)
return data.reduce((acc, item) =>
// assign variables
const week = Object.keys(item).pop();
const [job, value] = Object.entries(item[week]).pop();
// initialise them, if they aren't in the accumilator.
if (!acc[week]) acc[week] = ;
if (!acc[week][job]) acc[week][job] = 0;
// add the value
acc[week][job] += value;
return acc;
, );
console.log(cleanUp(data));
这是一个使用for...in
而不是Object.keys()
和Object.entries()
,
const data = [
"week 01": Developer: 1 ,
"week 01": "Project Manager": 5 ,
"week 01": "Project Manager": 6 ,
"week 01": Developer: 8 ,
"week 02": Strategy: 4
];
function cleanUp(data)
return data.reduce((acc, item) =>
for (const week in item)
if (!acc[week]) acc[week] = ;
for (const job in item[week])
if (!acc[week][job]) acc[week][job] = 0;
acc[week][job] += item[week][job];
return acc;
, );
console.log(cleanUp(data));
【讨论】:
【参考方案4】:使用嵌套循环。在纯 javascript 中你可能会得到类似的东西
const data = [ "week 01": "Developer": 1 ,
"week 01": "Project Manager": 5 ,
"week 01": "Project Manager": 6 ,
"week 01": "Developer": 8 ,
"week 02": "Strategy": 4 ];
function getTotalHoursPerRole(data)
let totalHoursPerRole = [];
for (let i = 0; i < data.length; i++)
for (let week in data[i])
let roleWithHour = data[i][week];
for (let role in roleWithHour)
if (!totalHoursPerRole[week]) totalHoursPerRole[week] = ;
if (!totalHoursPerRole[week][role]) totalHoursPerRole[week][role] = 0;
totalHoursPerRole[week][role] += roleWithHour[role];
return totalHoursPerRole;
console.log(getTotalHoursPerRole(data));
【讨论】:
【参考方案5】:const data = [
"week 01": Developer: 1 ,
"week 01": "Project Manager": 5 ,
"week 01": "Project Manager": 6 ,
"week 01": Developer: 8 ,
"week 02": Strategy: 4 ,
];
const result = data.reduce(
(prev, item) =>
for (const week in item)
if (prev[week])
for (const act in item[week])
if (prev[week][act])
prev[week][act] += item[week][act];
else
prev[week][act] = item[week][act];
else
prev[week] = item[week];
return prev;
,
);
console.log(result);
【讨论】:
这正是我想要实现的。当它全部用明文写出来时,看起来很简单。非常感谢您的帮助【参考方案6】:我会使用反射和嵌套循环而不是 reduce。
const weeks = ;
for(let cell of array)
//Assumes your cells will only have one key and that key is a week
for(let key in Object.keys(cell))
if(!weeks[key])
weeks[key] = ;
//Similiar assumption here
for(let role in Object.keys(cell[key]))
weeks[key][role] = (weeks[key][role] ? weeks[key][role] : 0) + cell[key][role];
【讨论】:
【参考方案7】:您可以使用所有类型的累积值生成单个对象。
const
data = [ "week 01": Developer: 1 , "week 01": "Project Manager": 5 , "week 01": "Project Manager" : 6 , "week 01": Developer : 8 , "week 02": Strategy: 4 ],
result = data.reduce((r, o) =>
Object
.entries(o)
.forEach(([week, q]) => Object
.entries(q)
.forEach(([type, value]) =>
(r[week] ??= )[type] ??= 0;
r[week][type] += value;
)
);
return r;
, );
console.log(result);
.as-console-wrapper max-height: 100% !important; top: 0;
【讨论】:
以上是关于使用动态键合并和求和对象数组的主要内容,如果未能解决你的问题,请参考以下文章
如何根据javascript中的键合并和替换两个数组中的对象?