【问题标题】:nested reduce function for objects对象的嵌套reduce函数
【发布时间】:2020-06-29 15:06:42
【问题描述】:

我正在尝试减少已经减少的数组,并且在第二次减少功能之后我的对象出现问题。我在第二个函数运行之前的对象如下所示:

const object = {
     groupA: [
       {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupA'},
       {name: "Eric", age: 25, Job: "Vet", group: 'groupA'},
       {name: "Bob", age: 25, Job: "Pharmacist", group: 'groupA'},
       {name: "Peter", age: 25, Job: "Vet", group: 'groupA'},
     ],
    groupB: [
       {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupB'},
       {name: "Eric", age: 25, Job: "Vet", group: 'groupB'},
       {name: "Bob", age: 25, Job: "Pharmacist", group: 'groupB'},
       {name: "Peter", age: 25, Job: "Vet", group: 'groupB'},
     ],    


 }

如果我想得到这样的结果,我应该使用什么函数。


const object = {
     groupA: {
       pharmacist: [
         {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupA'},
         {name: "Bob", age: 25, Job: "Pharmacist", group: 'groupA'},
      ],
      vet: [
       {name: "Eric", age: 25, Job: "Vet", group: 'groupA'},
       {name: "Peter", age: 25, Job: "Vet", group: 'groupA'},
      ]
     },
     groupB: {
       pharmacist: [
         {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupB'},
         {name: "Bob", age: 25, Job: "Pharmacist", group: 'groupB'},
      ],
       vet: [
        {name: "Eric", age: 25, Job: "Vet", group: 'groupB'},
        {name: "Peter", age: 25, Job: "Vet", group: 'groupB'},
      ]
     }, 


 }

【问题讨论】:

  • 嘿@piotr.schaar。如果我假设您的初始数据结构是一个对象数组? (在减少后看起来就像您在问题中所看到的那样)。然后我实际上可以为整个问题提供解决方案,而不仅仅是第二部分。您可以致电groupBy(input, ["group", "Job"]),它将返回您想要的输出。您也可以将其称为嵌套,例如 groupBy(input, ["group", "Job", "age"]) - stackoverflow.com/a/60741156/9792594
  • 我的代码确实有一个错误,这意味着它只能工作到 2 级深度分组,但现在它可以用于 N 级深度 :) stackoverflow.com/a/60741156/9792594

标签: javascript arrays object reduce


【解决方案1】:

您可以为每个组创建一个单独的函数reduce(),然后使用for..in 将每个组的未缩减数组替换为缩减对象

const object = {"groupA":[{"name":"Adam","age":25,"Job":"Pharmacist","group":"groupA"},{"name":"Eric","age":25,"Job":"Vet","group":"groupA"},{"name":"","age":25,"Job":"Pharmacist","group":"groupA"},{"name":"","age":25,"Job":"Vet","group":"groupA"}],"groupB":[{"name":"Adam","age":25,"Job":"Pharmacist","group":"groupB"},{"name":"Eric","age":25,"Job":"Vet","group":"groupB"},{"name":"","age":25,"Job":"Pharmacist","group":"groupB"},{"name":"","age":25,"Job":"Vet","group":"groupB"}]}

function groupByJob(arr){
  return arr.reduce((ac, a) => {
    if(!ac[a.Job]){
      ac[a.Job] = [];
    }
    ac[a.Job].push(a);
    return ac;
  }, {});
}

for(let k in object){
  object[k] = groupByJob(object[k]);
}
console.log(object)

如果您不想拆分,可以直接在 object 的条目上应用 map(),然后在每个值上直接使用 map()

const object = {"groupA":[{"name":"Adam","age":25,"Job":"Pharmacist","group":"groupA"},{"name":"Eric","age":25,"Job":"Vet","group":"groupA"},{"name":"","age":25,"Job":"Pharmacist","group":"groupA"},{"name":"","age":25,"Job":"Vet","group":"groupA"}],"groupB":[{"name":"Adam","age":25,"Job":"Pharmacist","group":"groupB"},{"name":"Eric","age":25,"Job":"Vet","group":"groupB"},{"name":"","age":25,"Job":"Pharmacist","group":"groupB"},{"name":"","age":25,"Job":"Vet","group":"groupB"}]}

const res = Object.fromEntries(
               Object.entries(object)
                 .map(([k, v]) => 
                    [  
                       k, 
                       v.reduce((ac, a) => 
                           (ac[a.Job] = (ac[a.Job] || []).concat(a), ac), 
                       {})
                    ]
                  )
                )

console.log(res)

【讨论】:

    【解决方案2】:

    您可以使用嵌套的reduce 方法创建具有按作业分组值的新对象。

    const object = {"groupA":[{"name":"Adam","age":25,"Job":"Pharmacist","group":"groupA"},{"name":"Eric","age":25,"Job":"Vet","group":"groupA"},{"name":"","age":25,"Job":"Pharmacist","group":"groupA"},{"name":"","age":25,"Job":"Vet","group":"groupA"}],"groupB":[{"name":"Adam","age":25,"Job":"Pharmacist","group":"groupB"},{"name":"Eric","age":25,"Job":"Vet","group":"groupB"},{"name":"","age":25,"Job":"Pharmacist","group":"groupB"},{"name":"","age":25,"Job":"Vet","group":"groupB"}]}
    
    const result = Object.entries(object).reduce((r, [k, v]) => {
      r[k] = v.reduce((r, e) => {
        if (!r[e.Job]) r[e.Job] = [e]
        else r[e.Job].push(e)
        return r
      }, {})
    
      return r;
    }, {})
    
    
    console.log(result)

    【讨论】:

      【解决方案3】:

      您可以通过使用Object.entries() 然后将.map() 每个值数组获取到基于键Job 的分组对象来获取object 的条目。要对数组值进行分组,您可以使用.reduce(),方法是累积一个具有数组值的对象,其中包含每个关联Job 的对象。然后,您可以使用 Object.fromEntries() 从映射条目构建您的结果对象:

      const object = { groupA: [ {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupA'}, {name: "Eric", age: 25, Job: "Vet", group: 'groupA'}, {name: "Bob", age: 25, Job: "Pharmacist", group: 'groupA'}, {name: "Peter", age: 25, Job: "Vet", group: 'groupA'}, ], groupB: [ {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupB'}, {name: "Eric", age: 25, Job: "Vet", group: 'groupB'}, {name: "Bob", age: 25, Job: "Pharmacist", group: 'groupB'}, {name: "Peter", age: 25, Job: "Vet", group: 'groupB'}, ], }
       
      const res = Object.fromEntries(Object.entries(object).map(
        ([key, arr]) => [key, arr.reduce((acc, obj) => {
          acc[obj.Job] = [...(acc[obj.Job] || []), obj];
          return acc;
        }, {})]
      ));
      
      console.log(res);

      如果您不支持Object.fromEntries(),您可以将.map() 用于对象而不是[key, value] 对数组,然后将结果传播到Object.assign(),如下所示:

      const object = { groupA: [ {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupA'}, {name: "Eric", age: 25, Job: "Vet", group: 'groupA'}, {name: "Bob", age: 25, Job: "Pharmacist", group: 'groupA'}, {name: "Peter", age: 25, Job: "Vet", group: 'groupA'}, ], groupB: [ {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupB'}, {name: "Eric", age: 25, Job: "Vet", group: 'groupB'}, {name: "Bob", age: 25, Job: "Pharmacist", group: 'groupB'}, {name: "Peter", age: 25, Job: "Vet", group: 'groupB'}, ], }
       
      const res = Object.assign({}, ...Object.entries(object).map(
        ([key, arr]) => ({[key]: arr.reduce((acc, obj) => {
          acc[obj.Job] = [...(acc[obj.Job] || []), obj];
          return acc;
        }, {})})
      ));
      
      console.log(res);

      【讨论】:

        【解决方案4】:

        我们可以这样做(嵌套reduce):

        const input = {
             groupA: [
               {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupA'},
               {name: "Eric", age: 25, Job: "Vet", group: 'groupA'},
               {name: "Bob", age: 26, Job: "Pharmacist", group: 'groupA'},
               {name: "Peter", age: 26, Job: "Vet", group: 'groupA'},
             ],
            groupB: [
               {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupB'},
               {name: "Eric", age: 25, Job: "Vet", group: 'groupB'},
               {name: "Bob", age: 26, Job: "Pharmacist", group: 'groupB'},
               {name: "Peter", age: 26, Job: "Vet", group: 'groupB'},
             ]
         }
         
         const output = (property) => {
          return Object.entries(input).reduce((aggObj, [key,val]) => {
            const grouped = val.reduce((groupedObj, item) => {
              if (groupedObj.hasOwnProperty(item[property])){
                groupedObj[item[property]].push(item);
              } else{
                groupedObj[item[property]] = [item];
              }          
              return groupedObj;
            }, {});        
            aggObj[key] = grouped;        
            return aggObj;
          }, {})
         }
         console.log(output('Job'));
         //we could also do the same for age:
         //console.log(output('age'));
        
        /* Desired output: */
        /*
        const output = {
             groupA: {
               pharmacist: [
                 {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupA'},
                 {name: "Bob", age: 25, Job: "Pharmacist", group: 'groupA'},
              ],
              vet: [
               {name: "Eric", age: 25, Job: "Vet", group: 'groupA'},
               {name: "Peter", age: 25, Job: "Vet", group: 'groupA'},
              ]
             },
             groupB: {
               pharmacist: [
                 {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupB'},
                 {name: "Bob", age: 25, Job: "Pharmacist", group: 'groupB'},
              ],
               vet: [
                {name: "Eric", age: 25, Job: "Vet", group: 'groupB'},
                {name: "Peter", age: 25, Job: "Vet", group: 'groupB'},
              ]
             }, 
        
        
         }
         */

        本质上,使用 Object.entries(input) 从 Object 中进行迭代

        然后遍历这些 ([key, val]) 条目并提取密钥(即首先是“GroupA”然后是“GroupB”)

        这些值(val)中的每一个都是一个数组,因此我们可以对其进行归约(即val.reduce()

        然后将每个匹配 item.Job 的数组推送到分组对象中。

        然后将 this 赋值给最终返回的聚合对象和结尾。

        更新 - groupBy 的递归解决方案,嵌套最多 N 层

        我想让它真正通用,并假设您的初始数据看起来如何,并允许您在其他属性上进行这样的嵌套分组(根据需要嵌套)。该功能显示在答案的下方(即 3 级深度分组),但现在,这是您的具体示例:

        你可以这样称呼它groupBy(input, ["group", "Job"])

        结果按组分组,然后按作业(嵌套)。

        对于您的具体示例:

        //assumed initial input, array of objects:
        const input = [
          { name: "Adam", age: 25, Job: "Pharmacist", group: "groupA" },
          { name: "Eric", age: 25, Job: "Vet", group: "groupA" },
          { name: "Bob", age: 26, Job: "Pharmacist", group: "groupA" },
          { name: "Peter", age: 26, Job: "Vet", group: "groupA" },
          { name: "Adam", age: 25, Job: "Pharmacist", group: "groupB"},
          { name: "Eric", age: 25, Job: "Vet", group: "groupB" },
          { name: "Bob", age: 26, Job: "Pharmacist", group: "groupB" },
          { name: "Peter", age: 26, Job: "Vet", group: "groupB" }
        ];
        
        const groupBy = (input, propertyArr) => {
          //console.log(propertyArr);
          const property = propertyArr[0];
          const grouped = input.reduce((groupedObj, item) => {
            groupedObj[item[property]] = [...(groupedObj[item[property]] || []), item];
            return groupedObj;
          }, {});
          if (propertyArr.length > 1) {
            //console.log(grouped);    
            return Object.keys(grouped).reduce((AggObj, key, index) => {
              const propertyArrCopy = [...propertyArr];
              propertyArrCopy.shift();
              AggObj[key] = groupBy(grouped[key], propertyArrCopy);
              return AggObj;
            }, {});
          } else {
            return grouped;
          }
        };
        console.log(groupBy(input, ["group", "Job"]));
        .as-console-wrapper { max-height: 100% !important; top: 0; }

        使用groupBy(input, ["group", "Job"])我们得到你期望的输出:

        {
          "groupA": {
            "Pharmacist": [
              {"name": "Adam", "age": 25, "Job": "Pharmacist", "group": "groupA"},
              {"name": "Bob", "age": 26, "Job": "Pharmacist", "group": "groupA"}
            ],
            "Vet": [
              {"name": "Eric", "age": 25, "Job": "Vet", "group": "groupA"},
              {"name": "Peter", "age": 26, "Job": "Vet", "group": "groupA"}
            ]
          },
          "groupB": {
            "Pharmacist": [
              {"name": "Adam", "age": 25, "Job": "Pharmacist", "group": "groupB"},
              {"name": "Bob", "age": 26, "Job": "Pharmacist", "group": "groupB"}
            ],
            "Vet": [
              {"name": "Eric", "age": 25, "Job": "Vet", "group": "groupB"},
              {"name": "Peter", "age": 26, "Job": "Vet", "group": "groupB"}
            ]
          }
        }
        

        具有 3 级深度分组的更通用解决方案的能力示例:

        即打电话给groupBy(input, ["group", "Job", "age"])

        结果按组分组,然后按作业,然后按年龄(嵌套)。

        //assumed initial input, array of objects:
        const input = [
          { name: "Adam", age: 25, Job: "Pharmacist", group: "groupA" },
          { name: "Lauren", age: 25, Job: "Pharmacist", group: "groupA" },
          { name: "Eric", age: 25, Job: "Vet", group: "groupA" },
          { name: "Theresa", age: 25, Job: "Vet", group: "groupA" },
          { name: "Bob", age: 26, Job: "Pharmacist", group: "groupA" },
          { name: "Brandy", age: 26, Job: "Pharmacist", group: "groupA" },
          { name: "Alex", age: 26, Job: "Scientist", group: "groupA" },
          { name: "Tom", age: 26, Job: "Scientist", group: "groupA" },
          { name: "Peter", age: 26, Job: "Vet", group: "groupA" },
          { name: "Kate", age: 26, Job: "Vet", group: "groupA" },
          { name: "Adam", age: 25, Job: "Pharmacist", group: "groupB" },
          { name: "Sarah", age: 25, Job: "Pharmacist", group: "groupB" },
          { name: "Eric", age: 25, Job: "Vet", group: "groupB" },
          { name: "Sophie", age: 25, Job: "Vet", group: "groupB" },
          { name: "Bob", age: 26, Job: "Pharmacist", group: "groupB" },
          { name: "Anne", age: 26, Job: "Pharmacist", group: "groupB" },
          { name: "Peter", age: 26, Job: "Vet", group: "groupB" },
          { name: "Mary", age: 26, Job: "Vet", group: "groupB" },
          { name: "Alex", age: 26, Job: "Scientist", group: "groupB" },
          { name: "Sarah", age: 26, Job: "Scientist", group: "groupB" }
        ];
        
        const groupBy = (input, propertyArr) => {
          //console.log(propertyArr);
          const property = propertyArr[0];
          const grouped = input.reduce((groupedObj, item) => {
            groupedObj[item[property]] = [...(groupedObj[item[property]] || []), item];
            return groupedObj;
          }, {});
          if (propertyArr.length > 1) {
            //console.log(grouped);    
            return Object.keys(grouped).reduce((AggObj, key, index) => {
              const propertyArrCopy = [...propertyArr];
              propertyArrCopy.shift();
              AggObj[key] = groupBy(grouped[key], propertyArrCopy);
              return AggObj;
            }, {});
          } else {
            return grouped;
          }
        };
        console.log(groupBy(input, ["group", "Job", "age"]));
        .as-console-wrapper { max-height: 100% !important; top: 0; }

        我在初始数组中添加了一些对象以使其更有趣。这也可以扩展到更大的数据集和更多级别的嵌套,并且应该可以正常工作。

        【讨论】:

          【解决方案5】:

          您可以使用过滤器和减少

          const object = {
               groupA: [
                 {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupA'},
                 {name: "Eric", age: 25, Job: "Vet", group: 'groupA'},
                 {name: "Name", age: 25, Job: "Pharmacist", group: 'groupA'},
                 {name: "Name", age: 25, Job: "Vet", group: 'groupA'},
               ],
              groupB: [
                 {name: "Adam", age: 25, Job: "Pharmacist", group: 'groupB'},
                 {name: "Eric", age: 25, Job: "Vet", group: 'groupB'},
                 {name: "Name", age: 25, Job: "Pharmacist", group: 'groupB'},
                 {name: "Name", age: 25, Job: "Vet", group: 'groupB'},
               ],    
           }
           
           for(const key in object){
             object[key] = object[key]
             .map(a => a.Job)
             .filter((v,i,c)=> c.indexOf(v) === i)
             .reduce((acc, i) => ({...acc, [i]: object[key].filter(p => p.Job === i)}), {})
           }
           
           console.log(object)

          【讨论】:

            【解决方案6】:

            编写函数arrToObj 将数组转换为具有分组键的对象。
            将上述方法应用于对象中的所有条目并构建一个新对象。

            const object = {
              groupA: [
                { name: "Adam", age: 25, Job: "Pharmacist", group: "groupA" },
                { name: "Eric", age: 25, Job: "Vet", group: "groupA" },
                { name: "Bob", age: 25, Job: "Pharmacist", group: "groupA" },
                { name: "Peter", age: 25, Job: "Vet", group: "groupA" }
              ],
              groupB: [
                { name: "Adam", age: 25, Job: "Pharmacist", group: "groupB" },
                { name: "Eric", age: 25, Job: "Vet", group: "groupB" },
                { name: "Bob", age: 25, Job: "Pharmacist", group: "groupB" },
                { name: "Peter", age: 25, Job: "Vet", group: "groupB" }
              ]
            };
            
            const arrToObj = arr => {
              const res = {};
              arr.forEach(item => {
                if (!res[item.Job]) {
                  res[item.Job] = [];
                }
                res[item.Job].push(item);
              });
              return res;
            };
            
            const newObject = Object.fromEntries(
              Object.entries(object).map(([key, value]) => [key, arrToObj(value)])
            );
            
            console.log(newObject);

            【讨论】:

              猜你喜欢
              • 2021-11-06
              • 2023-03-14
              • 1970-01-01
              • 2021-08-13
              • 2016-05-18
              • 2018-04-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多