Fares K. A.
Fares K. A.

Reputation: 1285

JavaScript: Grouping Objects w/ Similar Attributes

I have an array of JavaScript objects, that look something like this.

[ { name: "A", value: 50 },
  { name: "B", value: 20 }, 
  { name: "C", value: 30 }, 
  { name: "A", value: 40 }, 
  { name: "A", value: 50 }, 
  { name: "B", value: 70 }, 
  { name: "A", value: 10 }, 
  { name: "C", value: 50 } ]

I want to loop through this array and remove any duplicate objects of the same name, while adding their values together. In basic terms:

[ { name: "A", value: 200 },
  { name: "B", value: 90  }, 
  { name: "C", value: 80  } ]

I have an idea of how to go about this, pseudo-code:

for each object Obj in array A
    current = Obj
    for(i = (Obj's index + 1); i < A.length; i++)
        if(A[i].name = Obj.name) 
            Obj.value += A[i].value
            delete A[i]
        endif
    endfor
endfor

I feel this is very inefficient. Is there any better way of going about this?

Upvotes: 2

Views: 101

Answers (6)

Tomasz Pluskiewicz
Tomasz Pluskiewicz

Reputation: 3662

You can use the reduce function.

Note that for simplicity the sums are gathered as an associative array (grouped by name) and then the values retrieved to match your expected result.

var arr = [ { name: "A", value: 50 },
  { name: "B", value: 20 }, 
  { name: "C", value: 30 }, 
  { name: "A", value: 40 }, 
  { name: "A", value: 50 }, 
  { name: "B", value: 70 }, 
  { name: "A", value: 10 }, 
  { name: "C", value: 50 } ];

var sums = arr.reduce(function (acc, curr) { 
  if(acc[curr.name]) {
    acc[curr.name].value += curr.value;
  } else {
    acc[curr.name] = curr;
  }
  
  return acc;
}, {});

var res = Object.keys(sums).map(function (key) {
    return sums[key];
});

console.log(res);

Upvotes: 1

Thomas
Thomas

Reputation: 12637

What language is this? this ain't JS.

in JS I would write it like that:

array = array.filter(function(item){
    //check wether there's already an cached item for this name
    if(item.name in this){
        //then add the value to the cached item
        this[item.name].value += item.value;
        //and remove the current item from the result
        return false;
    }else{
        //otherwise cache this item
        this[item.name] = item;
        //and keep it in the result
        return true;
    }
}, {/* abusing `this` as a cache */});

Upvotes: -1

Tomasz Lewowski
Tomasz Lewowski

Reputation: 1965

You're looking for reduce and object-based indexing:

var arr = [ { name: "A", value: 50 },
  { name: "B", value: 20 }, 
  { name: "C", value: 30 }, 
  { name: "A", value: 40 }, 
  { name: "A", value: 50 }, 
  { name: "B", value: 70 }, 
  { name: "A", value: 10 }, 
  { name: "C", value: 50 } ]

var accumulated = arr.reduce(function(accumulator, element) {
     var currentValue = accumulator[element.name];
     if(currentValue !== undefined) {
         accumulator[element.name] = currentValue + element.value;
     } else {
         accumulator[element.name] = element.value;
     }
     return accumulator;
}, {})

var result = Object.keys(accumulated).map(function(k) {return {name: k, value: accumulated[k]}})
console.log(result)

Upvotes: 0

Nenad Vracar
Nenad Vracar

Reputation: 122027

You can do this with forEach() loop and thisArg parameter.

var data = [ { name: "A", value: 50 },
  { name: "B", value: 20 }, 
  { name: "C", value: 30 }, 
  { name: "A", value: 40 }, 
  { name: "A", value: 50 }, 
  { name: "B", value: 70 }, 
  { name: "A", value: 10 }, 
  { name: "C", value: 50 } ];
  
var result = [];
data.forEach(function(e) {
  if (!this[e.name]) {
    this[e.name] = e;
    result.push(this[e.name]);
  } else {
    this[e.name].value += e.value;
  }
}, {});

console.log(result)

Upvotes: 1

TAGraves
TAGraves

Reputation: 1409

I would use a temporary object as an intermediary state, and then create a new array based on that object:

var arr = [ { name: "A", value: 50 },
  { name: "B", value: 20 }, 
  { name: "C", value: 30 }, 
  { name: "A", value: 40 }, 
  { name: "A", value: 50 }, 
  { name: "B", value: 70 }, 
  { name: "A", value: 10 }, 
  { name: "C", value: 50 } ];

var tempObj = arr.reduce(function (newObj, obj) {
    if (newObj[obj.name]) {
        newObj[obj.name] += obj.value;
    } else {
        newObj[obj.name] = obj.value;
    }
    return newObj;
}, {});

console.log(tempObj);

var result = [];

for (var i in tempObj) {
    result.push({
        name: i,
        value: tempObj[i]
    });
}

console.log(result);

Upvotes: 3

Patrick Barr
Patrick Barr

Reputation: 1123

You could create a temporary "associative array" and iterate through the array of objects like

var temp = [];
for(var i = 0; i < A.length; i++) {
    temp[A[i].name] += A[i].value;
}
A = temp;

If you're trying to keep A as an actual array, then look at @TAGraves answer for the extra bit to convert it into an actual array.

Upvotes: 0

Related Questions