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? 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.

Sign up to request clarification or add additional context in comments.

Comments

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

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.