Sum javascript object propertyA values with same object propertyB in array of objects

How would one take a javascript array of objects such as:

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}]

and merge duplicate keys by summing the values. In order to get something like this:

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}]

I have tried iterating and adding to a new array, but this didn't work:

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

Answers:

Answer

You should be assigning each object not found to the result with its .key property.

If it is found, then you need to add its .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]);

Also, part of the problem was that you were reusing the item variable to reference the value of .key, so you lost reference to the object.

Answer

Rather than using a for loop and pushing values, you can directly use map and reduce:

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

Answer

You could use a hash table for the grouping by 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; }

Another approach is to collect all key/value pairs in a Map and format the final array with Array.from and a callback for the objects.

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

Answer
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/

Answer

Try this, it should help

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

Below is another solution that uses only one loop (while loop):

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

Simpler reduce than posted elsewhere

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)

Answer

Here is an alternative for you, but similar to that of Explosion Pills, reuses the original array rather than creating a new one or a different object. The sort may not be necessary and will slow things down a little, but it could be removed.

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

And a jsperf that compares this (with and without the sort) against the accepted answer. You can improve the performance test by extending the data set.

Answer

you can also try using javascript linq framework which is exactly same as sql statement which is given desired output with less written code and effective and found at 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>

which is pretty easy as compare to looping. i hope this may help.

Answer

Recently l needed a similar implementation and l used a similar solution offered by some guy that used reduce function. Few days later i wanted to implement something similar by myself and here is the result.

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

Output:

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

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us Javascript

©2020 All rights reserved.