Muskan
Muskan

Reputation: 53

I've an array of objects need to sum on basis of key suffix

This is the original array:

[
    {
      id: robin, 
      savings: 5500,
      cost: 1200
    },
    {
     id: robin_1,
     savings: 50,
     cost: 100
    },
    {
    id: robin_2,
     savings: 50,
     cost: 150
    },
    {
      id: steve, 
      savings: 100,
      cost: 1000
    },
    {
     id: steve_1,
     savings: 50,
     cost: 550
    },
    {
    id: steve_2,
     savings: 50,
     cost: 150
    },
]

I'm trying to get this below expected output .

{
 robin :{
   allTime:{
      savings: 5500,
      cost: 1200,
    },
   today:{
     savings: 100,
     cost: 250 
    }
  },
  steve:{
   allTime: {
     savings:100,
     cost: 1000
   },
   today: {
     savings: 100,
     cost: 700
   }
  }
}

Basically when id is only robin set the output in allTime key and when there are some suffixes after robin like 1,2,3 then sum them and set it into today.

Have tried by normally looping them and groupby with _.sumBy() like below attempt but have failed.

var output = _.groupBy(resultSet, value => value.id)
               .map((objs, key) => (
                { 'id': key,
                  'savings': _.sumBy(objs,'savings'), 
                  'cost': _.sumBy(objs, 'cost'))
                }
                .value();

Upvotes: 0

Views: 59

Answers (3)

secan
secan

Reputation: 2679

I think I'd go with Array.prototype.reduce() like this:

const input = [
  { id: 'robin', savings: 5500, cost: 1200 },
  { id: 'robin_1', savings: 50, cost: 100 },
  { id: 'robin_2', savings: 50, cost: 150 },
  { id: 'steve', savings: 100, cost: 1000 },
  { id: 'steve_1', savings: 50, cost: 550 },
  { id: 'steve_2', savings: 50, cost: 150 },
  { id: 'bob', savings: 1000, cost: 300 },
  { id: 'mark_1', savings: 50, cost: 150 }
];

const output = input.reduce((outObj, item) => {
  const { savings, cost } = item;
  const [mainKey, suffix] = item.id.split('_');
  const subKey = suffix === undefined ? 'allTime' : 'today';

  if (!outObj.hasOwnProperty(mainKey)) {
    outObj[mainKey] = {
      [subKey]: { savings, cost }
    }
  } else {
    if (!outObj[mainKey].hasOwnProperty(subKey)) {
      outObj[mainKey][subKey] = { savings, cost }
    } else {
      outObj[mainKey][subKey].savings += savings;
      outObj[mainKey][subKey].cost += cost;
    }
  }

  return outObj;
}, {})

//test
console.log(output);

With this approach you do not "hard-code" the properties allTime and today into each entry of the output object; instead you dynamically add them only if/where they are needed (see the examples with bob and mark where only allTime or today is added)

Upvotes: 0

lejlun
lejlun

Reputation: 4419

I managed to do it like this using array#reduce:

let data = [
    { id: 'robin', savings: 5500, cost: 1200 },
    { id: 'robin_1', savings: 50, cost: 100 },
    { id: 'robin_2', savings: 50, cost: 150 },
    { id: 'steve', savings: 100, cost: 1000 },
    { id: 'steve_1', savings: 50, cost: 550 },
    { id: 'steve_2', savings: 50, cost: 150 },
];

let newData = data.reduce((acc, { id, savings, cost }) => {
    let [pre, suff] = id.split('_');

    if (!acc[pre]) acc[pre] = { allTime: {}, today: { savings: 0, cost: 0 } };
    if (!suff) acc[pre].allTime = { savings, cost };
    else (acc[pre].today.savings += savings), (acc[pre].today.cost += cost);

    return acc;
}, {});

console.log(newData);

And like this using a for...of loop:

let data = [
    { id: 'robin', savings: 5500, cost: 1200 },
    { id: 'robin_1', savings: 50, cost: 100 },
    { id: 'robin_2', savings: 50, cost: 150 },
    { id: 'steve', savings: 100, cost: 1000 },
    { id: 'steve_1', savings: 50, cost: 550 },
    { id: 'steve_2', savings: 50, cost: 150 },
];

let newData = {};
for ({ id, savings, cost } of data) {
    let [pre, suff] = id.split('_');

    if (!newData[pre]) newData[pre] = { allTime: {}, today: { savings: 0, cost: 0 } };
    if (!suff) newData[pre].allTime = { savings, cost };
    else (newData[pre].today.savings += savings), (newData[pre].today.cost += cost);
}

console.log(newData)

Upvotes: 2

RK_oo7
RK_oo7

Reputation: 527

Try this looped by reduce

const data = [
    {
      id: "robin", 
      savings: 5500,
      cost: 1200
    },
    {
     id: "robin_1",
     savings: 50,
     cost: 100
    },
    {
    id: "robin_2",
     savings: 50,
     cost: 150
    },
    {
      id: "steve", 
      savings: 100,
      cost: 1000
    },
    {
     id: "steve_1",
     savings: 50,
     cost: 550
    },
    {
    id: "steve_2",
     savings: 50,
     cost: 150
    },
];
    const res = data.reduce((acc, val) => {
          const splitVal =  val.id.split("_")[0];
          if(acc[val.id] || acc[splitVal]){
               acc[splitVal].today.savings+=val.savings;
               acc[splitVal].today.cost+=val.cost;
          }else{
               acc[val.id] = {
                    allTime:{
                         savings: val.savings,
                         cost: val.cost,
                    },
                    today:{
                         savings: 0,
                         cost: 0 
                    }
               }
          }
          return acc;
    }, {});
    console.log(res);

Upvotes: 0

Related Questions