What is the best way to reduce and merge a collection of objects

Viewed 74

So I reached a dilemma this week while trying to solve what seemed to be an easy problem, but I was wrong.

Sample code:

let arr = [
  { id: 'ae0123', cost: 100.00, orders: 5 },
  { id: 'ae0123', cost: 50.00, orders: 5 },
  { id: 'ae4321', cost: 75.00, orders: 1 }
]

So the problem was being able to map through the array and merge/sum the values if their unique identifiers match.

Any suggestions?

End result would look something like this:

[
  { id: 'ae0123', cost: 150.00, orders: 10 },
  { id: 'ae4321', cost: 75.00, orders: 1 }
]

Now of course the real application will not have statically typed ids, but they were written into this example. So please take that into consideration.

Thanks in advance!

7 Answers

You can use a Map to store objects by id, and add the values if it already exists in it:

let arr = [
  { id: 'ae0123', cost: 100.00, orders: 5 },
  { id: 'ae0123', cost: 50.00, orders: 5 },
  { id: 'ae4321', cost: 75.00, orders: 1 }
];
let map = {};
for(let i = 0; i < arr.length; i++){
     let item = arr[i];
     if(map[item.id]){
          map[item.id].cost += item.cost;
          map[item.id].orders += item.orders;
     }else{
          map[item.id] = item;
     }
}
console.log(Object.values(map));

You could take an object for grouping and an array for adding the wanted properties.

let array = [{ id: 'ae0123', cost: 100.00, orders: 5 }, { id: 'ae0123', cost: 50.00, orders: 5 }, { id: 'ae4321', cost: 75.00, orders: 1 }],
    result = Object.values(array.reduce((r, o) => {
        if (r[o.id]) {
            ['cost', 'orders'].forEach(k => r[o.id][k] += o[k]);
        } else {       
            r[o.id] = { ...o };
        }
        return r;
    }, {}));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Create a new array

For each item, if it already exists then add the cost and orders, else add it to the array.

let arr = [
  { id: 'ae0123', cost: 100.00, orders: 5 },
  { id: 'ae0123', cost: 50.00, orders: 5 },
  { id: 'ae4321', cost: 75.00, orders: 1 }
]

let newArr = []

arr.forEach(item => {
  let existingItem = newArr.find(x => x.id == item.id);
  if (existingItem != undefined) {
    existingItem.cost += item.cost;
    existingItem.orders += item.orders;
  } else {
    newArr.push(item);
  }
});

console.log(newArr);

let arr = [
  { id: 'ae0123', cost: 100.00, orders: 5 },
  { id: 'ae0123', cost: 50.00, orders: 5 },
  { id: 'ae4321', cost: 75.00, orders: 1 }
]

let obj={};

arr.forEach(item=>{
  if(obj[item.id]){
    obj[item.id].cost +=item.cost;
    obj[item.id].orders +=item.orders;
  }
  else{
    obj[item.id]=item;
  }
});

const mappedArr=Object.keys(obj).map(key=>obj[key]);

console.log(mappedArr)

Try this

I'd go with reduce method of Array object, the above mentioned solution of using literal object works as well, but I think for such case, reduce makes code more readable and elegant:

const arr = [
  { id: 'ae0123', cost: 100.00, orders: 5 },
  { id: 'ae0123', cost: 50.00, orders: 5 },
  { id: 'ae4321', cost: 75.00, orders: 1 }
];

const newArr = arr.reduce((acc, obj) => {
  if (!acc[obj.id]) {
    acc[obj.id] = obj;
  } else {
    acc[obj.id].cost += obj.cost;
    acc[obj.id].orders += obj.orders;
  }
  
  return acc;
}, {});

console.log( Object.values(newArr) );

Probably this is what you want

let arr = [
  { id: 'ae0123', cost: 100.00, orders: 5 },
  { id: 'ae0123', cost: 50.00, orders: 5 },
  { id: 'ae4321', cost: 75.00, orders: 1 }
]

let uniq = []
arr.map(x => {
  if(!uniq.includes(x.id)) uniq = [...uniq, x.id]
})

uniq = uniq.map(x => {
  let filtered = arr.filter(y => y.id === x)
  let res = filtered.shift()
  for(const z of filtered) {
    const {cost, orders} = z
    res.cost += cost
    res.orders += orders
  }
  return res
})

console.log(uniq)

let newArr = []

arr.forEach((el, ind) => { 
    let uniqueId = el.id;
    let obj = el;
    let multiple = false;
    arr.forEach((e, i) => {
        if(e.id == uniqueId && ind != i ) {
            el.cost += e.cost;
            el.orders += e.orders;
             multiple = true;
             arr.splice(i,1);
             newArr.push(el)
        }
    });

    if(!multiple){
        newArr.push(obj);
    }

});

Tell me if it works, this will create a new array with the objects.

Related