0

I have this array of data and need a remapped version of it to iterate through the dates.

var objArr = [
     {"Country": "US", "2017-07-12": "1", "2017-07-13": "2"},
     {"Country": "US", "2017-07-12": "2", "2017-07-13": "2"},
     {"Country": "CN",  "2017-07-12": "5", "2017-07-13": "7"},
     {"Country": "CN", "2017-07-12": "5", "2017-07-13": "7"}
   ]
  • The countries should sum their values and should not appear twice in the inner array.
  • At the end I wanna have an array like this:
     var remapped = [
       {"2017-07-12": [
                    {"Country: "US", "value": 3}, 
                    {"Country: "CN", "value": 10}],
       {"2017-07-13": [
                    {"Country: "US", "value": 4},
                    {"Country: "US", "value": 14}]
     ]

In my current case, I get this, but countries should get reduced and the value summed:

var remapped = [
   {"2017-07-12": [
                {"Country: "US", "value": 1}, 
                {"Country: "US", "value": 2}, 
                {"Country: "CN", "value": 5}],
                {"Country: "CN", "value": 5}
                  ],
   {"2017-07-13": [
                {"Country: "US", "value": 2},
                {"Country: "US", "value": 2},
                {"Country: "US", "value": 7}
                {"Country: "US", "value": 7}
                 ]
 ]

This is what I've so far. But for me, it seems overcomplicated, and I am sure that there is a far more efficient way to solve that: At this state I have the countries remapped into the dates, but not sure how to "reduce" them. Do I really have to multiple iterates over the arrays?

const res = [];
dateArray.forEach(date => (objArr[date] = [])); // I create an array with dates which could contain another array

objArr.forEach(item => {
  dates.forEach(date => {
    res[date].push({
      Country: item['Country'],
      value: item[date],
    });
  });
});
Jan
  • 12,992
  • 9
  • 53
  • 89

3 Answers3

0

You could do this using reduce method and first store values for each date in one object so you can group them by Country and then convert those objects to arrays.

var data = [
 {"Country": "US", "2017-07-12": "1", "2017-07-13": "2"},
 {"Country": "US", "2017-07-12": "2", "2017-07-13": "2"},
 {"Country": "CN",  "2017-07-12": "5", "2017-07-13": "7"},
 {"Country": "CN", "2017-07-12": "5", "2017-07-13": "7"}
]

const result = data.reduce((r, {Country, ...rest}, i) => {
  Object.entries(rest).forEach(([k, v]) => {
    if(!r[k]) r[k] = {}
    if(!r[k][Country]) r[k][Country] = {Country, value: +v}
    else r[k][Country].value += +v
  })
  
  return r;
}, {})

for(let i in result) {
  result[i] = Object.values(result[i])
}

console.log(result)
Nenad Vracar
  • 118,580
  • 15
  • 151
  • 176
  • This output seems a much more logical format than the one requested, but note that it is different. It's asking for an array of one-value objects. – Scott Sauyet Mar 04 '20 at 15:01
0

You could take an helper object for the references of the objects and iterate the key/value pairs of the given data.

var data = [{ Country: "US", "2017-07-12": "1", "2017-07-13": "2" }, { Country: "US", "2017-07-12": "2", "2017-07-13": "2" }, { Country: "CN", "2017-07-12": "5", "2017-07-13": "7" }, { Country: "CN", "2017-07-12": "5", "2017-07-13": "7" }],
    reference = {},
    result = data.reduce((r, { Country, ...o }) => {
        Object.entries(o).forEach(([k, v]) => {
            if (!reference[k]) {
                reference[k] = { _: [] };
                r.push({ [k]: reference[k]._ });
            }
            if (!reference[k][Country]) {
                reference[k]._.push(reference[k][Country] = { Country, value: 0 });
            }
            reference[k][Country].value += +v;
        });
        return r;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

Here is another approach:

const extractDates = objArr => Object .entries (objArr .reduce (
  (a, {Country, ...rest}) => Object .entries (rest) .reduce ((a, [date, val]) => ({
    ...a,
    [date]: {...a [date], [Country]: ((a [date] || {}) [Country] || 0) + Number (val)}
  }), a),
  {}
)) .map (([d, o]) => ({[d]: Object .entries (o) .map (([k, v]) => ({Country: k, value: v}))}))

const objArr = [{"Country": "US", "2017-07-12": "1", "2017-07-13": "2"}, {"Country": "US", "2017-07-12": "2", "2017-07-13": "2"}, {"Country": "CN",  "2017-07-12": "5", "2017-07-13": "7"}, {"Country": "CN", "2017-07-12": "5", "2017-07-13": "7"}]

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

This gives the result you asked for in the question, an array of one-key elements. But note that it's built around this core:

objArr .reduce (
  (a, {Country, ... rest}) => Object .entries (rest) .reduce ((a, [date, val]) => ({
    ...a,
    [date]: {... a [date], [Country]: ((a [date] || {}) [Country] || 0) + Number (val)} 
  }), a),
  {}
)

which gives what to me is a much more useful data structure:

{
  "2017-07-12": {CN: 10, US: 3}, 
  "2017-07-13": {CN: 14, US: 4}
}

While the final structure (without the Object .entries (...) .map (...) wrapper) could be built in a single pass (see for instance Nina's answer; the one from Nenand Vracar yields a slightly different structure, I find this breakdown cleaner, and think that the intermediate format would in most cases be easier to use.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103