Home > Back-end >  Combine objects in an array with the same id but sum totals
Combine objects in an array with the same id but sum totals

Time:12-02

I currently have an array of objects that looks like this:

const orders = [
  { id: '1', userId: '3', total: 300 },
  { id: '2', userId: '4', total: 200 },
  { id: '3', userId: '5', total: 101 },
  { id: '4', userId: '6', total: 80 },
  { id: '5', userId: '7', total: 76 },
  { id: '6', userId: '8', total: 44 },
  { id: '7', userId: '9', total: 1000 },
  { id: '8', userId: '10', total: 99 },
  { id: '9', userId: '3', total: 65 },
  { id: '10', userId: '4', total: 22 }
];
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

How would I combine the totals for any object in the array that shares the same userId?

I would like my outcome to look like this:

const newOrdersArray = [
  { id: '1', userId: '3', total: 365 },
  { id: '2', userId: '4', total: 222 },
  { id: '3', userId: '5', total: 101 },
  { id: '4', userId: '6', total: 80 },
  { id: '5', userId: '7', total: 76 },
  { id: '6', userId: '8', total: 44 },
  { id: '7', userId: '9', total: 1000 },
  { id: '8', userId: '10', total: 99 }
];
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

I don't know the use case behind this, but looking at the data, I assume that the id is the id of the specific order. It feels somehow odd to keep this in the cumulated result, where the total is only in relation to all orders of the user and not any longer to one single order. Therefore I would just drop this and calculate the total like this:

totals = orders.reduce((totals, order) => {
  totals[order.userId] = (totals[order.userId] ?? 0)   order.total;
  return totals;
}, {});

Yes, you don't have an array any longer but an object with the userIds as key instead. If this does not fit your specific use case, simply transform it again.

CodePudding user response:

Here's a way using reduce

const orders = [
  { id: '1', userId: '3', total: 300 },
  { id: '2', userId: '4', total: 200 },
  { id: '3', userId: '5', total: 101 },
  { id: '4', userId: '6', total: 80 },
  { id: '5', userId: '7', total: 76 },
  { id: '6', userId: '8', total: 44 },
  { id: '7', userId: '9', total: 1000 },
  { id: '8', userId: '10', total: 99 },
  { id: '9', userId: '3', total: 65 },
  { id: '10', userId: '4', total: 22 }
];

let combined = orders.reduce((b, a) => {
  let index = b.findIndex(arr => arr.userId == a.userId);
  delete a.id
  if (index > -1) b[index].total  = a.total;
  else b.push(a);
  return b;
}, [])

console.log(combined);
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

Try this:

const orders = [
  { id: '1', userId: '3', total: 300 },
  { id: '2', userId: '4', total: 200 },
  { id: '3', userId: '5', total: 101 },
  { id: '4', userId: '6', total: 80 },
  { id: '5', userId: '7', total: 76 },
  { id: '6', userId: '8', total: 44 },
  { id: '7', userId: '9', total: 1000 },
  { id: '8', userId: '10', total: 99 },
  { id: '9', userId: '3', total: 65 },
  { id: '10', userId: '4', total: 22 }
];

const totals = [];
orders.forEach(x => {
  const obj = totals.find(o => o.userId === x.userId);
  if (obj) {
    obj.total = obj.total   x.total;
  } else {
    totals.push(x);
  }
});

console.log(totals);
<iframe name="sif4" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

You could take an object for addressing same userId and get the values from the object as result.

const
    orders = [{ id: '1', userId: '3', total: 300 }, { id: '2', userId: '4', total: 200 }, { id: '3', userId: '5', total: 101 }, { id: '4', userId: '6', total: 80 }, { id: '5', userId: '7', total: 76 }, { id: '6', userId: '8', total: 44 }, { id: '7', userId: '9', total: 1000 }, { id: '8', userId: '10', total: 99 }, { id: '9', userId: '3', total: 65 }, { id: '10', userId: '4', total: 22 }],
    result = Object.values(orders.reduce((r, o) => {
        r[o.userId] ??= { ...o, total: 0 };
        r[o.userId].total  = o.total;
        return r;
    }, {}));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<iframe name="sif5" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

Here is a variation of the .reduce() approach in which the IDs are collected in arrays for each userID:

const orders = [
  { id: '1', userId: '3', total: 300 },
  { id: '2', userId: '4', total: 200 },
  { id: '3', userId: '5', total: 101 },
  { id: '4', userId: '6', total: 80 },
  { id: '5', userId: '7', total: 76 },
  { id: '6', userId: '8', total: 44 },
  { id: '7', userId: '9', total: 1000 },
  { id: '8', userId: '10', total: 99 },
  { id: '9', userId: '3', total: 65 },
  { id: '10', userId: '4', total: 22 }
];

let combined = Object.values(orders.reduce((a,c) => { 
  let e = (a[c.userId] = a[c.userId] || {ids:[],userId:c.userId,total:0});
  e.total =c.total;
  e.ids.push(c.id);
  return a;
}, {}))

console.log(combined);
<iframe name="sif6" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related