【问题标题】:Sum javascript object propertyA values with same object propertyB in array of objects在对象数组中对具有相同对象属性的 javascript 对象属性值求和
【发布时间】:2013-10-14 12:35:51
【问题描述】:

如何获取一个 javascript 对象数组,例如:

my objArr = [
{key:Mon Sep 23 2013 00:00:00 GMT-0400, val:42},
{key:Mon Sep 24 2013 00:00:00 GMT-0400, val:78},
{key:Mon Sep 25 2013 00:00:00 GMT-0400, val:23},
{key:Mon Sep 23 2013 00:00:00 GMT-0400, val:54}]

并通过对值求和来合并重复的键。 为了得到这样的东西:

my reducedObjArr = [
{key:Mon Sep 23 2013 00:00:00 GMT-0400, val:96},
{key:Mon Sep 24 2013 00:00:00 GMT-0400, val:78},
{key:Mon Sep 25 2013 00:00:00 GMT-0400, val:23}]

我已经尝试迭代并添加到一个新数组,但这不起作用:

var reducedObjArr = [];
var item = null, key = null;
for(var i=0; i<objArr.length; i++) {
   item=objArr[i];
   key = Object.keys(item)[0];
   item=item[key];

   if(!result[key]){
       result[key] = item;
   }else{
       result[key] += item;}
   }a

【问题讨论】:

  • 你为什么要key = Object.keys(item)[0]; item=item[key];?您已经知道名称是 key,所以只需使用 item.keyobjArr[i].key。此外,使用[0] 索引不一定总是给您相同的属性。
  • 感谢寻找相同的,,

标签: javascript arrays json object reduce


【解决方案1】:
var targetObj = {};
for (var i = 0; i < objArr.length; i++) {
    if (!targetObj.hasOwnProperty(objArr[i].key)) {
        targetObj[objArr[i].key] = 0;
    }
    targetObj[objArr[i].key] += objArr[i].val;
}

http://jsfiddle.net/HUMxp/

【讨论】:

  • OP 想要相同的键/值对,但合并了 val
【解决方案2】:

您应该使用其.key 属性将每个未找到的对象分配给结果。

如果找到,则需要添加其.val

var temp = {};
var obj = null;
for(var i=0; i < objArr.length; i++) {
   obj=objArr[i];

   if(!temp[obj.key]) {
       temp[obj.key] = obj;
   } else {
       temp[obj.key].val += obj.val;
   }
}
var result = [];
for (var prop in temp)
    result.push(temp[prop]);

另外,部分问题在于您重用了item 变量来引用.key 的值,因此您丢失了对该对象的引用。

【讨论】:

    【解决方案3】:

    这里为您提供了另一种选择,但类似于Explosion Pills,它重用原始数组而不是创建新数组或不同对象。排序可能不是必需的,并且会减慢速度,但可以删除。

    Javascript

    function reduceMyObjArr(arr) {
        var temp = {},
            index;
    
        for (index = arr.length - 1; index >= 0; index -= 1) {
            key = arr[index].key;
            if (temp.hasOwnProperty(key)) {
                arr[temp[key]].val += arr[index].val;
                arr.splice(index, 1);
            } else {
                temp[key] = index;
            }
        }
    
        arr.sort(function (a, b) {
            if (a.key === b.key) {
                return 0;
            }
    
            if (a.key < b.key) {
                return -1;
            }
    
            return 1;
        });
    
        return arr;
    }
    
    var myObjArr = [{
        key: "Mon Sep 23 2013 00: 00: 00 GMT - 0400",
        val: 42
    }, {
        key: "Mon Sep 24 2013 00: 00: 00 GMT - 0400",
        val: 78
    }, {
        key: "Mon Sep 25 2013 00: 00: 00 GMT - 0400",
        val: 23
    }, {
        key: "Mon Sep 23 2013 00: 00: 00 GMT - 0400",
        val: 54
    }];
    
    reduceMyObjArr(myObjArr);
    
    console.log(myObjArr);
    

    jsFiddle

    还有一个jsperf 将这个(有和没有排序)与接受的答案进行比较。您可以通过扩展数据集来改进性能测试。

    【讨论】:

      【解决方案4】:

      您可以直接使用mapreduce,而不是使用for循环和推送值:

      let objArr = [
        {key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 42},
        {key: 'Mon Sep 24 2013 00:00:00 GMT-0400', val: 78},
        {key: 'Mon Sep 25 2013 00:00:00 GMT-0400', val: 23},
        {key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 54}
      ];
      
      // first, convert data into a Map with reduce
      let counts = objArr.reduce((prev, curr) => {
        let count = prev.get(curr.key) || 0;
        prev.set(curr.key, curr.val + count);
        return prev;
      }, new Map());
      
      // then, map your counts object back to an array
      let reducedObjArr = [...counts].map(([key, value]) => {
        return {key, value}
      })
      
      console.log(reducedObjArr);

      【讨论】:

      • 是否也可以使用 map/reduce 添加单独的计数。示例:9 月 23 日,计数为 2,24 日和 25 日计数为 1。
      • 是的!您只需在 prev.set 调用中执行 count + 1 之类的操作,而不是 curr.val + count
      【解决方案5】:

      您也可以尝试使用与 sql 语句完全相同的 javascript linq 框架,该框架以更少的编写代码和有效的方式提供所需的输出,并且可以在 linq.js 中找到

      var objArr = 
      [
      {key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:42},
      {key:'Mon Sep 24 2013 00:00:00 GMT-0400', val:78},
      {key:'Mon Sep 25 2013 00:00:00 GMT-0400', val:23},
      {key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:54}
      ];
      
      
      var aggregatedObject = Enumerable.From(objArr)
              .GroupBy("$.key", null,
                       function (key, g) {
                           return {
                             key: key,
                             contributions: g.Sum("$.val")
                           }
              })
              .ToArray();
      
      console.log(aggregatedObject);
      &lt;script src="http://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.min.js"&gt;&lt;/script&gt;

      与循环相比,这很容易。 我希望这可能会有所帮助。

      【讨论】:

        【解决方案6】:

        您可以使用哈希表按key 进行分组。

        var array = [{ key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 42 }, { key: 'Mon Sep 24 2013 00:00:00 GMT-0400', val: 78 }, { key: 'Mon Sep 25 2013 00:00:00 GMT-0400', val: 23 }, { key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 54}],
            grouped = [];
        
        array.forEach(function (o) {
            if (!this[o.key]) {
                this[o.key] = { key: o.key, val: 0 };
                grouped.push(this[o.key]);
            }
            this[o.key].val += o.val;
        }, Object.create(null));
        
        console.log(grouped);
        .as-console-wrapper { max-height: 100% !important; top: 0; }

        另一种方法是收集Map 中的所有键/值对,并使用Array.from 和对象回调来格式化最终数组。

        var array = [{ key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 42 }, { key: 'Mon Sep 24 2013 00:00:00 GMT-0400', val: 78 }, { key: 'Mon Sep 25 2013 00:00:00 GMT-0400', val: 23 }, { key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 54 }],
            grouped = Array.from(
                array.reduce((m, { key, val }) => m.set(key, (m.get(key) || 0) + val), new Map),
                ([key, val]) => ({ key, val })
            );
        
        console.log(grouped);
        .as-console-wrapper { max-height: 100% !important; top: 0; }

        【讨论】:

        • 如何添加多个键?
        • @klent,将this[o.key].val += o.val; 中的val 替换为您要添加的属性。
        • 对不起,我的意思是如何在添加之前添加另一个条件,例如,如果他们的另一个属性称为“类别”。我想检查键和类别。
        • 获取要分组的键数组,例如groups = ['foo', 'bar'],在forEach 中使用var key = groups.map(k =&gt; o[k]).join('|');,然后使用this[key] 而不是this[o.key]。可能完全省略this 并为对象使用全局变量进行分组。
        • 如果我的 obj 类似于 obj = [ { menu: "apple", amount: 3}, { menu: "apple", amount: 1}, {menu: "melon", amount: 10}, {menu: "stawberry", amount: 7} ]; 我想要输出是 [ {menu: apple, total: 4}, {menu: melon, total: 10}, and.......... ]@NinaScholz,如何在对象中求和相同的值
        【解决方案7】:

        最近我需要一个类似的实现,我使用了一个使用reduce函数的人提供的类似解决方案。几天后,我想自己实现类似的东西,结果如下。

        const users = [
                { id: 1, name: 'ernest', spent: 40 }, 
                { id: 2, name: 'ernest', spent: 40 },
                { id: 3, name: 'astrit', spent: 22 },
                { id: 4, name: 'astrit', spent: 2956 },
                { id: 5, name: 'astrit', spent: 22 }, 
                { id: 6, name: 'besart', spent: 40 }, 
                { id: 7, name: 'besart', spent: 100}, 
                { id: 8, name: 'besart', spent: 4000 }
            ];
        
                const sum = [];
        
                users.forEach(el => {
                    if(sum.length === 0){
                        delete el.id;
                        sum.push(el);
                    }    
                    else
                    {
                        const get = () => {
                            for(let i = 0; i < sum.length; i++){
                                if(sum[i].name === el.name ){
                                    return { stat: true, id: i };
                                }
                            }
                        }
        
                        let i = get();
                        if(i){
                            sum[i.id].spent += el.spent;
                        }
                        else
                        {
                            delete el.id;
                            sum.push(el);
                        }
                    }
                });
        
                console.log(sum);
        

        输出:

        [ { name: 'ernest', spent: 80 }, { name: 'astrit', spent: 3000 }, { name: 'besart', spent: 4140 } ]
        

        【讨论】:

          【解决方案8】:

          试试这个,应该会有帮助

          var arr1 = [
              { name: 'besart', value: 12 },
              { name: 'astrit', value: 10 },
              { name: 'astrit', value: 10 },
              { name: 'besar', value: 18 },
              { name: 'besar', value: 3 },
              { name: 'astrit', value: 3 },
              { name: 'besart', value: 3 },
              { name: 'besart', value: 10 },
              { name: 'besar', value: 0 },
          ];
          
          var arr2 = [];
          var emri = "";
          var value = 0;
          for(var i = 0;i<arr1.length;i++){
              emri=arr1[0].name;
              value+=arr1[0].value;
              for(var j=1;j<arr1.length;j++){
                  if(emri==arr1[j].name){
                      value+=arr1[j].value;
                      arr1.splice(j,1);
                      j--;
                  }
              }
              arr1.splice(0,1);
              arr2[i] = {name:emri,value:value};
              value=0;
          }
          console.log(arr2);
          

          下面是另一种只使用一个循环(while循环)的解决方案:

           var arr2 = [];
              var emri = "";
              var value = 0;
              var i=1;
              var j=0;
          
          while(arr1.length != 0){
                  emri = arr1[0].name;
                  if(emri == arr1[i].name){
                      value+=arr1[i].value;
                      arr1.splice(i,1);
                      i--;
                  }
                  i++;
                  if(i==arr1.length){
                      value+=arr1[0].value;
                      i=1;
                      arr2[j]={name:emri,value:value};
                      j++;
                      value=0;
                      arr1.splice(0,1);
                  }
              }
          

          【讨论】:

            【解决方案9】:

            比发布在其他地方更简单

            const objArr = [
            {key:"Mon Sep 23 2013 00:00:00 GMT-0400", val:42},
            {key:"Mon Sep 24 2013 00:00:00 GMT-0400", val:78},
            {key:"Mon Sep 25 2013 00:00:00 GMT-0400", val:23},
            {key:"Mon Sep 23 2013 00:00:00 GMT-0400", val:54}];
            
            const output = objArr.reduce((accumulator, cur) => {
              let date = cur.key;
              let found = accumulator.find(elem => elem.key === date)
              if (found) found.val += cur.val;
              else accumulator.push(cur);
              return accumulator;
            }, []);
            
            console.log(output)

            【讨论】:

              【解决方案10】:
              function mergeDuplicatesBy(array, getKey, mergeWith) {
                const buff = {}
                array.forEach(function (arrayElement) {
                  const key = getKey(arrayElement)
                  const alreadyExistingVal = buff[key]
                  if (alreadyExistingVal) {
                    buff[key] = mergeWith(alreadyExistingVal, arrayElement)
                  } else {
                    buff[key] = arrayElement
                  }
                })
                return Object.values(buff)
              }
              
              mergeDuplicatesBy(
                arr,
                x => x.name,
                (x, y) => ({ name: x.name, foo: x.foo + y.foo })
              )
              

              【讨论】:

                【解决方案11】:

                可以将Array#reduce 与对象一起使用来存储每个键的值。

                let arr = [{key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:42},{key:'Mon Sep 24 2013 00:00:00 GMT-0400', val:78},{key:'Mon Sep 25 2013 00:00:00 GMT-0400', val:23},{key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:54}];
                let res = Object.values(arr.reduce((acc, curr)=>{
                  (acc[curr.key] = acc[curr.key] || {key: curr.key, val: 0}).val += curr.val;
                  return acc;
                }, {}));
                console.log(res);

                在较新的浏览器中,可以使用logical nullish assignment

                let arr = [{key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:42},{key:'Mon Sep 24 2013 00:00:00 GMT-0400', val:78},{key:'Mon Sep 25 2013 00:00:00 GMT-0400', val:23},{key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:54}];
                let res = Object.values(arr.reduce((acc, curr)=>{
                  (acc[curr.key] ??= {key: curr.key, val: 0}).val += curr.val;
                  return acc;
                }, {}));
                console.log(res);

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-10-24
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-02-27
                  • 2015-06-30
                  • 1970-01-01
                  相关资源
                  最近更新 更多