【问题标题】: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}}

标签: javascript arrays ecmascript-6 reduce


【解决方案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; }

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-06-29
              • 2022-11-28
              • 2021-10-09
              • 2011-10-29
              • 2020-08-10
              • 2015-11-28
              • 2021-07-12
              • 1970-01-01
              相关资源
              最近更新 更多