【问题标题】:Sum similar keys in an array of objects对对象数组中的相似键求和
【发布时间】:2014-08-18 03:29:32
【问题描述】:

我有一个对象数组,如下所示:

[
    {
        'name': 'P1',
        'value': 150
    },
    {
        'name': 'P1',
        'value': 150
    },
    {
        'name': 'P2',
        'value': 200
    },
    {
        'name': 'P3',
        'value': 450
    }
]

我需要将同名对象的所有值相加。 (可能还有其他数学运算,例如计算平均值。)对于上面的示例,结果将是:

[
    {
        'name': 'P1',
        'value': 300
    },
    {
        'name': 'P2',
        'value': 200
    },
    {
        'name': 'P3',
        'value': 450
    }
]

【问题讨论】:

标签: javascript arrays object


【解决方案1】:

首先遍历数组并将“名称”推送到另一个对象的属性中。如果属性存在,则将“值”添加到属性的值,否则将属性初始化为“值”。构建此对象后,遍历属性并将它们推送到另一个数组。

这里有一些代码:

var obj = [
    { 'name': 'P1', 'value': 150 },
    { 'name': 'P1', 'value': 150 },
    { 'name': 'P2', 'value': 200 },
    { 'name': 'P3', 'value': 450 }
];

var holder = {};

obj.forEach(function(d) {
  if (holder.hasOwnProperty(d.name)) {
    holder[d.name] = holder[d.name] + d.value;
  } else {
    holder[d.name] = d.value;
  }
});

var obj2 = [];

for (var prop in holder) {
  obj2.push({ name: prop, value: holder[prop] });
}

console.log(obj2);

希望这会有所帮助。

【讨论】:

  • 这是一个很老的问题。从那时起,我就开始使用 Lodash (lodash.com)。您可以轻松地使用 sumsumBy 数学函数来实现原始查询中的要求
  • 嗨,我的对象如下如何处理? var obj = [ { 'name': 'P1', 'value': [150, 11] }, { 'name': 'P1', 'value': [150, 12] }, { 'name': ' P2', '值': [200, 13] }, { '名称': 'P3', '值': [450, 14] } ];
【解决方案2】:

name 分组的 ES6 方法:

您可以使用.reduce() 将对象数组转换为Map。该 Map 具有键值对,其中每个键是 name,每个 value 是该特定 name 键的值的累积总和。然后,您可以使用Array.from() 轻松地将 Map 转换回数组,您可以在其中提供一个映射函数,该函数将获取 Map 的键/值并将它们转换为对象:

const arr = [ { 'name': 'P1', 'value': 150 }, { 'name': 'P1', 'value': 150 }, { 'name': 'P2', 'value': 200 }, { 'name': 'P3', 'value': 450 } ];

const res = Array.from(arr.reduce(
  (m, {name, value}) => m.set(name, (m.get(name) || 0) + value), new Map
), ([name, value]) => ({name, value}));
console.log(res);

不仅仅是name分组:

如果您有其他重叠属性,而不仅仅是name(两个对象中的键/值必须相同,它们才能“分组”),那么这种方法应该可以工作。它涉及遍历您的数组并将其减少为包含键值对的 Map。新 Map 的每个键都是您要分组的所有属性值的字符串,因此,如果您的对象键已经存在,那么您知道它是重复的,这意味着您可以将对象的当前值添加到存储的对象.最后,您可以使用Array.from() 将您的键值对映射转换为仅包含值的数组:

const arr = [{'name':'P1','value':150},{'name':'P1','value':150},{'name':'P2','value':200},{'name':'P3','value':450}];

const res = Array.from(arr.reduce((acc, {value, ...r}) => {
  const key = JSON.stringify(r);
  const current = acc.get(key) || {...r, value: 0};  
  return acc.set(key, {...current, value: current.value + value});
}, new Map).values());
console.log(res);

【讨论】:

  • 请问,除了使用'object.entries',还有没有更好的实现方式?
  • 对不起,如果你遇到这种类型的数据,它不起作用[{ id: 10, name: "abc", quantity: 2 }, { id: 10, name: "abc", quantity: 1 } ]
  • @BaiClassmateXiao 我也尝试了这个答案,这对满足我的要求很有帮助。我已经定制了这个。我还需要为多个键执行此操作..只需传递另一个参数值,value1 等等..
  • @SiddharthAgrawal 第一个例子是O(N),其中Narr的长度。第二个示例是O(NK),其中K 是数组中对象的(最大)属性数。如果您采用排序方法,您可以获得的最佳复杂度(基于比较的排序)是O(NlogN),它大于O(N) 解决方案
  • @SiddharthAgrawal 要计算平均值,您可以使用与上述相同的方法进行分组,但是。对于每个值,存储一个具有名称、值和计数的对象(您看到具有相同 name 的对象的次数),然后使用 array.from 的第二个参数将这些对象转换回您想要的格式,您可以在其中使用count 找到平均值。我做了this example
【解决方案3】:

您可以使用Array.reduce() 在每次迭代期间累积结果。

var arr = [{'name':'P1','value':150},{'name':'P1','value':150},{'name':'P2','value':200},{'name':'P3','value':450}];

var result = arr.reduce(function(acc, val){
    var o = acc.filter(function(obj){
        return obj.name==val.name;
    }).pop() || {name:val.name, value:0};
    
    o.value += val.value;
    acc.push(o);
    return acc;
},[]);

console.log(result);

【讨论】:

    【解决方案4】:

    我猜还有一个干净的解决方案

     
    
        var obj =  [
            { 'name': 'P1', 'value': 150 },
            { 'name': 'P1', 'value': 150 },
            { 'name': 'P2', 'value': 200 },
            { 'name': 'P3', 'value': 450 }
        ];
    
         var result = [];
    Array.from(new Set(obj.map(x => x.name))).forEach(x => {
    
        result.push(obj.filter(y => y.name === x).reduce((output,item) => {
            let val = output[x] === undefined?0:output[x];
            output[x] =  (item.value +  val); 
           return output;
        },{}));
    
     })
          
                console.log(result);

    如果需要保持对象结构不变,

    var obj =  [
        { 'name': 'P1', 'value': 150 },
        { 'name': 'P1', 'value': 150 },
        { 'name': 'P2', 'value': 200 },
        { 'name': 'P2', 'value': 1000 },
        { 'name': 'P3', 'value': 450 }
    ];
    
    let output = [];
    let result = {};
    let uniqueName = Array.from(new Set(obj.map(x => x.name)));
    uniqueName.forEach(n => {
       output.push(obj.filter(x => x.name === n).reduce((a, item) => {
          
           let val = a['name'] === undefined? item.value:a['value']+item.value;
            
            return {name:n,value:val};
        },{})
    );
    });
    
    console.log(output);

    【讨论】:

      【解决方案5】:

      由于某种原因,当我运行@Vignesh Raja 的代码时,我获得了“总和”,但也获得了重复的项目。因此,我必须删除重复项,如下所述。

      原始数组:

      arr=[{name: "LINCE-01", y: 70}, 
       {name: "LINCE-01", y: 155},
       {name: "LINCE-01", y: 210},
       {name: "MIRAFLORES-03", y: 232},
       {name: "MIRAFLORES-03", y: 267}]
      

      使用@VigneshRaja 的代码:

      var result = arr.reduce(function(acc, val){
          var o = acc.filter(function(obj){
              return obj.name==val.name;
          }).pop() || {name:val.name, y:0};
      
          o.y += val.y;
          acc.push(o);
          return acc;
      },[]);
      
      console.log(result);
      

      第一个结果:

      result: [{name: "LINCE-01", y: 435},
       {name: "LINCE-01", y: 435},
       {name: "LINCE-01", y: 435},
       {name: "MIRAFLORES-03", y: 499},
       {name: "MIRAFLORES-03", y: 499}]
      

      删除重复项:

      var finalresult = result.filter(function(itm, i, a) {
                                  return i == a.indexOf(itm);
                              });
      console.log(finalresult);
      

      终于如愿以偿:

      finalresult = [{name: "LINCE-01", y: 435},
       {name: "MIRAFLORES-03", y: 657}]
      

      问候,

      【讨论】:

        【解决方案6】:

        我已经定制了 Nick Parsons 先生的答案(感谢您的想法)。如果您需要对多个键值求和。

        var arr = [{'name':'P1','value':150,'apple':10},{'name':'P1','value':150,'apple':20},{'name':'P2','value':200,'apple':30},{'name':'P3','value':450,'apple':40}];
        
        var res = Object.values(arr.reduce((acc, {value,apple , ...r}) => {
          var key = Object.entries(r).join('-');
          acc[key] = (acc[key]  || {...r, apple:0,value: 0});
          return (acc[key].apple += apple, acc[key].value += value, acc);
        }, {}));
        
        console.log(res);
        

        【讨论】:

          【解决方案7】:

          (function () {
          var arr = [
              {'name': 'P1', 'age': 150},
              {'name': 'P1', 'age': 150},
              {'name': 'P2', 'age': 200},
              {'name': 'P3', 'age': 450}
          ];
          var resMap = new Map();
          var result = [];
          arr.map((x) => {
              if (!resMap.has(x.name))
                  resMap.set(x.name, x.age);
              else
                  resMap.set(x.name, (x.age + resMap.get(x.name)));
          })
          resMap.forEach((value, key) => {
              result.push({
                  name: key,
                  age: value
              })
          })
          console.log(result);
          })();

          【讨论】:

            【解决方案8】:
            let ary = [
                {
                    'key1': 'P1',
                    'key2': 150
                },
                {
                    'key1': 'P1',
                    'key2': 150
                },
                {
                    'key1': 'P2',
                    'key2': 200
                },
                {
                    'key1': 'P3',
                    'key2': 450
                }
            ]
            

            结果数组let newAray = []

            for (let index = 0; index < ary.length; index++) {
                    // console.log(ary[index].key1);
                
                    for (let index2 = index + 1; index2 < ary.length; index2++) {
                
                        if (ary[index2].key1 == ary[index].key1) {
                            console.log('match');
                            ary[index].key2 += ary[index2].key2;
                            newAry = ary.filter( val => val !== ary[index2]);
                
                            newAry = ary.filter(function (e) {
                                return  e !== ary[index2];
                            });
                
                        }
                    }
                }
            console.log(newAry)
            

            【讨论】:

              【解决方案9】:
              let arr = [
                      {'name':'P1','value':150,'apple':10},
                      {'name':'P1','value':150,'apple':20},
                      {'name':'P2','value':200,'apple':30},
                      {'name':'P2','value':600,'apple':30},
                      {'name':'P3','value':450,'apple':40}
              ];
              
              let obj = {}
              
              arr.forEach((item)=>{
                 if(obj[item.name]){
                      obj[item.name].value = obj[item.name].value + item.value
                 }else{
                     obj[item.name] = item
                 }
              })
              
              let valuesArr = Object.values(obj)
              console.log(valuesArr);
              

              输出

              [ { 名称:“P1”,值:300,苹果:10 },
              { 名称:“P2”,值:800,苹果:30 },
              { 名称:“P3”,值:450,苹果:40 } ]

              【讨论】:

              • 如果我打印原始数组 arr 它会影响原始数组 [[ { name: 'P1', value: 300, apple: 10 }, { name: 'P1', value: 150, apple: 20 }, { name: 'P2', value: 800, apple: 30 }, { name: 'P2', value: 600, apple: 30 }, { name: 'P3', value: 450, apple: 40 } ]
              【解决方案10】:

              let data= [
                              {
                                  'key1': 'P1',
                                  'key2': 150
                              },
                              {
                                  'key1': 'P1',
                                  'key2': 150
                              },
                              {
                                  'key1': 'P2',
                                  'key2': 200
                              },
                              {
                                  'key1': 'P3',
                                  'key2': 450
                              }
                          ]
                          
                           var holder = []
                                      data.forEach( index => {
                                              const data = holder.find( i => i.key1=== index.key1)
                                              if(!data){
                                                  holder.push({key1:index.key1,key2:index.key2})
                                              }else{
                                                  data.key2 = parseInt(data.key2) + parseInt(index.key2)
                                              }
                                      });
                          
                          console.log(holder);

              【讨论】:

                猜你喜欢
                • 2019-03-14
                • 1970-01-01
                • 2015-11-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-08-27
                • 2021-10-09
                相关资源
                最近更新 更多