user1935987
user1935987

Reputation: 3347

typescript merge 2 arrays of objects by combining values of the ojects with the same key

Is there a proper way to merge 2 arrays which are consist of the objects with the same (though not exactly) keys and corresponding numeric values?

As the result i need an array with the sum of objects where the values for the same keys are combined. Keys are dates (Date()). each date have corresponding number.

Example:

const series_0 = [
  {
    'value': 3000,
    'name': '2016-09-20T20:23:48.426Z'
  },
  {
    'value': 6000,
    'name': '2016-09-21T08:58:04.100Z'
  },
  {
    'value': 4000,
    'name': '2016-09-21T05:21:08.317Z'
  },
  {
    'value': 6000,
    'name': '2016-09-19T11:26:36.302Z'
  },
  {
    'value': 5000,
    'name': '2016-09-16T11:26:19.165Z'
  }
]

const series_1 = [
  {
    'value': 3000,
    'name': '2016-09-20T20:23:48.426Z'
  },
  {
    'value': 1500,
    'name': '2016-09-21T08:58:04.100Z'
  },
  {
    'value': 8000,
    'name': '2016-09-19T11:26:36.302Z'
  },
]

desired result:

let result = [
  {
    'value': 6000,
    'name': '2016-09-20T20:23:48.426Z'
  },
  {
    'value': 7500,
    'name': '2016-09-21T08:58:04.100Z'
  },
  {
    'value': 4000,
    'name': '2016-09-21T05:21:08.317Z'
  },
  {
    'value': 6000,
    'name': '2016-09-19T11:26:36.302Z'
  },
  {
    'value': 13000,
    'name': '2016-09-16T11:26:19.165Z'
  }
]

Thank you!

Upvotes: 0

Views: 3911

Answers (3)

Mark
Mark

Reputation: 92440

I think I would just make a Map object and push add the dates onto it as keys summing as I go along:

const series_0 = [{'value': 3000,'name': '2016-09-20T20:23:48.426Z'},{'value': 6000,'name': '2016-09-21T08:58:04.100Z'},{'value': 4000,'name': '2016-09-21T05:21:08.317Z'},{'value': 6000,'name': '2016-09-19T11:26:36.302Z'},{'value': 5000,'name': '2016-09-16T11:26:19.165Z'}]
const series_1 = [{'value': 3000,'name': '2016-09-20T20:23:48.426Z'},{'value': 1500,'name': '2016-09-21T08:58:04.100Z'},{'value': 8000,'name': '2016-09-19T11:26:36.302Z'},]

let result = [series_0, series_1].reduce((sums, series) => 
    series.reduce((sums, item) => sums.set(item.name, (sums.get(item.name) || 0) + item.value), sums)
, new Map)

result = Array.from(result.entries(), ([name, value]) => ({name, value}))
console.log(result)

Upvotes: 1

mhodges
mhodges

Reputation: 11116

You can do this by utilizing .reduce() and .map().

The idea is to create a map of each unique date and the running total of that date's value for all data sets passed into the function, which would look something like so:

{
  "my-date-1": 3000,
  "my-date-2": 7500,
  ...
}

Then, once you have your unique set of dates and their total values, you can simply map them into the output you desire.

The benefit of this approach is that it is dynamic in the sense that you can pass in as many data sets as you'd like, the data sets can be in any order with any length, and you can pass in which property you'd like to group by. In this case, I just passed in the two datasets and told it to group on the "name" property.

function combineProps(propToMatch, ...dataSets) {
  return Object.entries(dataSets.reduce((res, curr) => {
    // sum each unique propToMatch value with the object's .value
    curr.forEach(obj => {
      var prop = obj[propToMatch];
      // if prop doesn't exist yet, create it
      if (!res[prop]) {
        res[prop] = 0;
      }
      // add to rolling total
      res[prop] += obj.value;
    });
    // return updated object
    return res;
  }, {}))
   // then map to desired output format
  .map(([name, value]) => ({value, name}));
}

const series_0 = [
  {
    'value': 3000,
    'name': '2016-09-20T20:23:48.426Z'
  },
  {
    'value': 6000,
    'name': '2016-09-21T08:58:04.100Z'
  },
  {
    'value': 4000,
    'name': '2016-09-21T05:21:08.317Z'
  },
  {
    'value': 6000,
    'name': '2016-09-19T11:26:36.302Z'
  },
  {
    'value': 5000,
    'name': '2016-09-16T11:26:19.165Z'
  }
]

const series_1 = [
  {
    'value': 3000,
    'name': '2016-09-20T20:23:48.426Z'
  },
  {
    'value': 1500,
    'name': '2016-09-21T08:58:04.100Z'
  },
  {
    'value': 8000,
    'name': '2016-09-19T11:26:36.302Z'
  },
];



console.log(combineProps("name", series_0, series_1));

Upvotes: 1

connexo
connexo

Reputation: 56744

There's probably many ways to achieve that, here's my approach:

const series_0 = [{
  'value': 3000,
  'name': '2016-09-20T20:23:48.426Z'
}, {
  'value': 6000,
  'name': '2016-09-21T08:58:04.100Z'
}, {
  'value': 4000,
  'name': '2016-09-21T05:21:08.317Z'
}, {
  'value': 6000,
  'name': '2016-09-19T11:26:36.302Z'
}, {
  'value': 5000,
  'name': '2016-09-16T11:26:19.165Z'
}]

const series_1 = [{
  'value': 3000,
  'name': '2016-09-20T20:23:48.426Z'
}, {
  'value': 1500,
  'name': '2016-09-21T08:58:04.100Z'
}, {
  'value': 8000,
  'name': '2016-09-19T11:26:36.302Z'
}, ]


//make sure the longer array is the outer for-loop-variable
const s0 = series_0.length >= series_1.length ? series_0 : series_1;
const s1 = series_0.length >= series_1.length ? series_1 : series_0;

let res = [];

for (const s of s0) {
  let resObj = {name:  s.name, value: s.value};
  for (const t of s1) {
    if (s.name === t.name) {
      resObj.value+=t.value
    }
  }
  res.push(resObj);
}

console.log(res);

Upvotes: 2

Related Questions