2

I have a stupid problem that at first seems to be simple to solve, but turns out to be tricky.

I have an array of objects, each with two properties: id and value:

[ 
  {id: 2, value: 10}, 
  {id: 4, value: 3},
  {id: 2, value: 2},
  {id: 1, value: 15}
]

I want to write an algorithm that sums up the values of ones with similar id. My end result should be a new array with only the merged objects:

[ 
  {id: 2, value: 12}, 
  {id: 4, value: 3},
  {id: 1, value: 15}
]

I've tried the following, but it doesn't work:

var arr = [];
arr.push({id: 2, visit:10}); 
arr.push({id: 4, visit:3}); 
arr.push({id: 2, visit:2}); 
arr.push({id: 1, visit:15}); 

// Deep copy
var copy = jQuery.extend(true, [], arr);
var masterArr = [];

for (var i = 0; i < arr.length; i++) {
    var objArr = [];
    objArr.push(arr[i]);
        for (var j = copy.length-1; j > -1; j--) {
        if (arr[i].id === copy[j].id) {
            var q = copy.splice(j,1);
        }
    }
        masterArr.push(objArr);
}

My plan was to first gather all similar objects in separate arrays (objArr), sum them up and put them in an end array (masterArr). I use jquerys extend to make a deep copy (not a reference) and reverse iteration and splice to remove objects thats already been found as "duplicates".

This doesn't work! And it doesn't seem to be a very efficient mehtod to solve my problem. How could I do this? Performance isn't top priority but rather "nice to have"!

Thanks!

1
  • Olefrank, did any of our answers help you?
    – Cerbrus
    Commented Apr 29, 2014 at 7:37

3 Answers 3

1

You can do it like this:

// Assuming:
a = [{id: 2, value: 10}, {id: 4, value: 3}, {id: 2, value: 2}, {id: 1, value: 15}]

var b = {}, // Temporary variable;
    c = []; // This will contain the result;

// Build a id:value object ( {1: 15, 2: 12, 4: 3} )
a.map(function(current){b[current.id] = (b[current.id] || 0) + current.value});
for(var key in b){  // Form that into the desired output format.
    c.push({id: parseInt(key, 10), value: b[key]});
}

console.log(c);
/* [{id: 1, value: 15},
    {id: 2, value: 12},
    {id: 4, value: 3}]  */

I'm using parseInt(key, 10), since the keys are strings, you'll probably want them converted to integers again.

0
1
// First group the data based on id and sum the values
var temp = data.reduce(function(result, current) {
    result[current.id] = (result[current.id] || 0) + current.value;
    return result;
}, {});

// then recreate the objects with proper id and value properties
var result = [];
for (var key in temp) {
    result.push({
        id: parseInt(key, 10),
        value: temp[key]
    });
}

console.log(result);

Output

[ { id: 1, value: 15 },
  { id: 2, value: 12 },
  { id: 4, value: 3 } ]
1

The quickest approach loops over the array only once using Array.prototype.filter():

var tmp = {},
    result = arr.filter(function (el) {
        if (tmp.hasOwnProperty(el.id)) {
            tmp[el.id].visit += el.visit;
            return false;
        }
        else {
            tmp[el.id] = el;
            return true;
        }
    });

It also reuses the objects, though this renders the original array to contain inaccurate values. If this is a problem, you can modify the example to copy each object property to a new object.

Not the answer you're looking for? Browse other questions tagged or ask your own question.