20

一个人将如何采用一个 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
4

11 回答 11

16

您可以直接使用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);

于 2016-06-03T21:15:04.873 回答
15

您应该使用其.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,因此您丢失了对对象的引用。

于 2013-10-07T19:53:24.623 回答
6

比其他地方发布的更简单的减少

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)

于 2019-11-20T10:12:18.570 回答
5

您可以使用哈希表进行分组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; }

另一种方法是收集 a 中的所有键/值对,并使用对象的回调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; }

于 2017-01-13T19:39:40.330 回答
3
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/

于 2013-10-07T19:53:14.207 回答
2

试试这个,应该有帮助

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);
        }
    }
于 2019-08-06T08:57:13.137 回答
0

这是您的替代方案,但类似于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将这个(有和没有排序)与接受的答案进行比较。您可以通过扩展数据集来改进性能测试。

于 2013-10-07T21:00:43.823 回答
0

您也可以尝试使用与 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);
<script src="http://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.min.js"></script>

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

于 2016-07-10T18:32:04.557 回答
0

最近我需要一个类似的实现,我使用了一些使用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 } ]
于 2019-08-05T19:31:22.387 回答
0
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 })
)
于 2021-03-05T11:23:42.283 回答
0

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

在较新的浏览器中,可以使用逻辑无效赋值。

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

于 2021-04-13T17:05:46.433 回答