使用动态键合并和求和对象数组

Merge and sum array of objects with dynamic keys

我有一个对象数组的数据结构,其中包含为以下多个不同角色花费的小时数:

    [{"week 01" : {"Developer" : 1}}, 
    {"week 01" : {"Project Manager" : 5}}, 
    {"week 01" : {"Project Manager" : 6}}, 
    {"week 01": {"Developer" : 8}},
    {"week 02" : {"Strategy" : 4}}]

角色和周数是动态的(即我事先不知道 key/values),我想对同一角色和同一周的所有时间求和以获得以下输出:

[{"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; 
}

您可以使用所有类型的累积值生成单个对象。

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

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

我会使用反射和嵌套循环而不是 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];
        }
    }
    
}

使用嵌套循环。在纯 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));

这是一个简单的方法。希望评论有用。

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

按周数分组,然后按工作名称对每个周数分组并求和。

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
            }
        }
    ]
}

然后每组week,按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
    }
]

如果每个条目只有一个(参见第二个[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]));