cooldude101
cooldude101

Reputation: 1415

Merging array of object with 1 property name that is the same

I'm trying to merge every object with a duplicate 'name' property, and by merge, I mean adding their 'value' property together and removing the duplicate one.

For some reason my code doesn't give expected results:

    array = [ 
        { name: 'f', value: '10' },
        { name: '5', value: '10' },
        { name: 'f', value: '10' },
        { name: '5', value: '10' },
        { name: 'f', value: '10' },
        { name: '5', value: '10' },
    ]

    for (let i = 0; i < array.length; i++) {
        for (let y = 0; y < array.length; y++) {

            if (array[y].name == array[i].name) {

                array[i].value = parseInt(array[i].value) + parseInt(array[y].value)

                array.splice(y,1)
                
            }
        }
    }
    
    console.log(array)
    // gives result [ { name: '5', value: 30 }, { name: 'f', value: '10' } ]
    // expected results [ { name: '5', value: 30 }, { name: 'f', value: '30' } ]

Upvotes: 1

Views: 445

Answers (4)

JLRishe
JLRishe

Reputation: 101748

This is happening because you are not ensuring that y and i are different when you update the element at i and removing the one at y. So you having a situation where you are modifying an item and then immediately removing it.

There are much better ways to do this, but here is a simple way to fix what you already have:

let array = [ 
    { name: 'f', value: '10' },
    { name: '5', value: '10' },
    { name: 'f', value: '10' },
    { name: '5', value: '10' },
    { name: 'f', value: '10' },
    { name: '5', value: '10' },
]

for (let i = 0; i < array.length; i++) {
    for (let y = 0; y < array.length; y++) {
        //  v-- ensure y and i are not the same
        if (y !== i && array[y].name === array[i].name) {
             array[i].value = parseInt(array[i].value) + parseInt(array[y].value)
             array.splice(y,1)
        }
    }
}
   
console.log(array)

Upvotes: 0

Phil
Phil

Reputation: 164960

Looks like a job for Array.prototype.reduce and Array.prototype.map.

First thing to do is generate a Map of names to cumulative values.

Then you can convert that into an array of objects with the names and totals.

const array = [ 
    { name: 'f', value: '10' },
    { name: '5', value: '10' },
    { name: 'f', value: '10' },
    { name: '5', value: '10' },
    { name: 'f', value: '10' },
    { name: '5', value: '10' }
]

const totals = array.reduce((map, item) => map.set(
    item.name, parseInt(item.value) + (map.get(item.name) || 0)
), new Map())

const merged = Array.from(totals)
    .map(([name, value]) => ({ name, value }))

console.info('Merged', merged)


Note: Map iteration is in insertion order so "f" will be first, followed by "5". If you need to change the order, Array.prototype.sort should be employed.

Upvotes: 1

codeLover
codeLover

Reputation: 2592

The issue here is that you are iterating and updating the same array due to which when you remove the index the array indices shift and you miss few of the elements. Try the below code:

array = [ 
    { name: 'f', value: '10' },
    { name: '5', value: '10' },
    { name: 'f', value: '10' },
    { name: '5', value: '10' },
    { name: 'f', value: '10' },
    { name: '5', value: '10' },

]
newArray = [];
for (let i = 0; i < array.length; i++) {
    var found=false;
   for(let j= 0;j<newArray.length;j++)   {
        if(newArray[j].name == array[i].name)    {
            newArray[j].value = parseInt(array[i].value)+ parseInt(newArray[j].value);
            found = true;
            break;
        }
   }
   if(!found)    {
    newArray.push(array[i]);
    }
}

console.log(newArray);

Upvotes: 0

Ayush Gupta
Ayush Gupta

Reputation: 9305

Use a combination of reduceand map to do this:

let array = [
      { name: 'f', value: '10' },
      { name: '5', value: '10' },
      { name: 'f', value: '10' },
      { name: '5', value: '10' },
      { name: 'f', value: '10' },
      { name: '5', value: '10' },
    ];
    
    let resultObj = array.reduce((result, item) => {
      result[item.name] = (result[item.name] || 0) + (Number(item.value) || 0);
      return result;
    }, {});
    
    let resultArray = Object.getOwnPropertyNames(resultObj).map(name => {
      return {name, value: resultObj[name]}
    })
    
    console.log(resultArray)

Upvotes: 3

Related Questions