Guilherme Lirio
Guilherme Lirio

Reputation: 99

Combine arrays inside map according to label and sum data

I have an array with some streaming channel data, according to the shift (Morning and Afternoon).

I spent the night trying some functions like reduce, but I couldn't understand how it works and stagnated.

var array = [{"label":"manha","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"25.00"},{"Nenhuma":"0.00"},{"Netflix":"55.00"},{"Outro":"20.00"}]},{"label":"manha","data":[{"Amazon Prime":"50.00"},{"AppleTV":"0.00"},{"HBO Max":"25.00"},{"Nenhuma":"0.00"},{"Netflix":"25.00"},{"Outro":"0.00"}]},{"label":"manha","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"0.00"},{"Nenhuma":"50.00"},{"Netflix":"50.00"},{"Outro":"0.00"}]},{"label":"tarde","data":[{"Amazon Prime":"10.00"},{"AppleTV":"11.00"},{"HBO Max":"0.00"},{"Nenhuma":"50.00"},{"Netflix":"9.00"},{"Outro":"20.00"}]},{"label":"tarde","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"5.00"},{"Nenhuma":"25.00"},{"Netflix":"20.00"},{"Outro":"50.00"}]}]

I expect to have this output:

The sum total of all streaming channels, divided by the amount of objects value per shift (morning and afternoon)

[{"label":"manha","data":[{"Amazon Prime":"16.66"},{"AppleTV":"0.00"},{"HBO Max":"16.66"},{"Nenhuma":"16.66"},{"Netflix":"43.33"},{"Outro":"6.66"}]},{"label":"tarde","data":[{"Amazon Prime":"5.00"},{"AppleTV":"5.50"},{"HBO Max":"2.50"},{"Nenhuma":"36.00"},{"Netflix":"14.50"},{"Outro":"35.00"}]}]

I would be very grateful if you could help me, and if possible, explain.

Upvotes: 1

Views: 89

Answers (3)

A1exandr Belan
A1exandr Belan

Reputation: 4780

Another approach. I simply used hash grouping twice to create an intermediate object and create a final object at the end

const data = [{"label":"manha","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"25.00"},{"Nenhuma":"0.00"},{"Netflix":"55.00"},{"Outro":"20.00"}]},{"label":"manha","data":[{"Amazon Prime":"50.00"},{"AppleTV":"0.00"},{"HBO Max":"25.00"},{"Nenhuma":"0.00"},{"Netflix":"25.00"},{"Outro":"0.00"}]},{"label":"manha","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"0.00"},{"Nenhuma":"50.00"},{"Netflix":"50.00"},{"Outro":"0.00"}]},{"label":"tarde","data":[{"Amazon Prime":"10.00"},{"AppleTV":"11.00"},{"HBO Max":"0.00"},{"Nenhuma":"50.00"},{"Netflix":"9.00"},{"Outro":"20.00"}]},{"label":"tarde","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"5.00"},{"Nenhuma":"25.00"},{"Netflix":"20.00"},{"Outro":"50.00"}]}]

const groups = data.reduce((acc, { label, data }) => {
  acc[label] ??= {};
  data.forEach((item) => {
    const [[channel, value]] = Object.entries(item);
    acc[label][channel] ??= [];
    acc[label][channel].push(Number(value));
  });
  return acc;
}, {});

// groups
//
// {"manha": {
//    "Amazon Prime": [0, 50, 0"],
//    "AppleTV": [0, 0, 0],
//    "HBO Max": [25, 25, 0],
//    "Nenhuma": [0, 0, 50],
//    "Netflix": [55, 25,50],
//    "Outro": [20, 0, 0]},
// ...}

const getFixedAvg = (data) => {
  const average = data.reduce((avg, e) => avg + e) / data.length;
  return (Math.floor(average * 100) / 100) // fix 16.67 to 16.66
    .toFixed(2); 
};

const makeData = (dataObj) => Object.entries(dataObj)
  .map(([channel, dataValues]) => ({ [channel]: getFixedAvg(dataValues) }));
  
const result = Object.entries(groups)
  .map(([label, dataObj]) => ({ label, data: makeData(dataObj) }));

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

Upvotes: 1

Stephen Quan
Stephen Quan

Reputation: 25956

Let's solve this problem in steps. The first attempt is using reduce to iterate through the result and build something. For simplicity let's get it to build an object where the keys represents the labels. The reason for this is we can use this key to merge similar keys together. On the TODO we observe that:

  • The numerical values are strings not number and do not lend itself for arithmetic operations
  • The subarray is difficult to merge and could do with a conversion to object as well

let array = [{"label":"manha","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"25.00"},{"Nenhuma":"0.00"},{"Netflix":"55.00"},{"Outro":"20.00"}]},{"label":"manha","data":[{"Amazon Prime":"50.00"},{"AppleTV":"0.00"},{"HBO Max":"25.00"},{"Nenhuma":"0.00"},{"Netflix":"25.00"},{"Outro":"0.00"}]},{"label":"manha","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"0.00"},{"Nenhuma":"50.00"},{"Netflix":"50.00"},{"Outro":"0.00"}]},{"label":"tarde","data":[{"Amazon Prime":"10.00"},{"AppleTV":"11.00"},{"HBO Max":"0.00"},{"Nenhuma":"50.00"},{"Netflix":"9.00"},{"Outro":"20.00"}]},{"label":"tarde","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"5.00"},{"Nenhuma":"25.00"},{"Netflix":"20.00"},{"Outro":"50.00"}]}];
let result = array.reduce( function (p, c, i, a) {
   let label = c.label;
   if (!(label in p)) {
       p[label] = c.data;
   }
   return p;
}, { } );
console.log(JSON.stringify(result, undefined, 2));

// Output:
// {
//   "manha": [ { "Amazon Prime": "0.00" },
//              { "AppleTV": "0.00" },
//              { "HBO Max": "25.00" },
//              { "Nenhuma": "0.00" },
//              { "Netflix": "55.00" },
//              { "Outro": "20.00" }
//            ],
//   "tarde": [ { "Amazon Prime": "10.00" },
//              { "AppleTV": "11.00" },
//              { "HBO Max": "0.00" },
//              { "Nenhuma": "50.00" },
//              { "Netflix": "9.00" },
//              { "Outro": "20.00" }
//            ]
// }

For the second attempt, we will:

  • convert more arrays to objects
    • as an object we can make use of object methods such as in to determine whether a key exists or not
  • we can use parseFloat to convert strings to numbers
  • we can start collating the information together This produces a nice set of raw data.

let array = [{"label":"manha","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"25.00"},{"Nenhuma":"0.00"},{"Netflix":"55.00"},{"Outro":"20.00"}]},{"label":"manha","data":[{"Amazon Prime":"50.00"},{"AppleTV":"0.00"},{"HBO Max":"25.00"},{"Nenhuma":"0.00"},{"Netflix":"25.00"},{"Outro":"0.00"}]},{"label":"manha","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"0.00"},{"Nenhuma":"50.00"},{"Netflix":"50.00"},{"Outro":"0.00"}]},{"label":"tarde","data":[{"Amazon Prime":"10.00"},{"AppleTV":"11.00"},{"HBO Max":"0.00"},{"Nenhuma":"50.00"},{"Netflix":"9.00"},{"Outro":"20.00"}]},{"label":"tarde","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"5.00"},{"Nenhuma":"25.00"},{"Netflix":"20.00"},{"Outro":"50.00"}]}];
let result = array.reduce( function (p, c, i, a) {
   let label = c.label;
   if (!(label in p)) {
       p[label] = { };
   }
   for (let i = 0; i < c.data.length; i++) {
       let e = Object.entries(c.data[i]);
       let k = e[0][0];
       let v = parseFloat(e[0][1]);
       if (!(k in p[label])) {
           p[label][k] = [ ];
       }
       p[label][k].push(v);
   }
   return p;
}, { } );
console.log(JSON.stringify(result, undefined, 2));

// Output:
// {
//   "manha": {
//     "Amazon Prime": [ 0, 50, 0 ],
//     "AppleTV":      [ 0, 0, 0 ],
//     "HBO Max":      [ 25, 25, 0 ],
//     "Nenhuma":      [ 0, 0, 50 ],
//     "Netflix":      [ 55, 25, 50 ],
//     "Outro":        [ 20, 0, 0 ]
//   },
//   "tarde": {
//     "Amazon Prime": [ 10, 0 ],
//     "AppleTV": [ 11, 0 ],
//     "HBO Max": [ 0, 5 ],
//     "Nenhuma": [ 50, 25 ],
//     "Netflix": [ 9, 20 ],
//     "Outro": [ 20, 50 ]
//   }
// }

For the final step, we just average out the remaining array and convert the numerical result back to a string.

let array = [{"label":"manha","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"25.00"},{"Nenhuma":"0.00"},{"Netflix":"55.00"},{"Outro":"20.00"}]},{"label":"manha","data":[{"Amazon Prime":"50.00"},{"AppleTV":"0.00"},{"HBO Max":"25.00"},{"Nenhuma":"0.00"},{"Netflix":"25.00"},{"Outro":"0.00"}]},{"label":"manha","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"0.00"},{"Nenhuma":"50.00"},{"Netflix":"50.00"},{"Outro":"0.00"}]},{"label":"tarde","data":[{"Amazon Prime":"10.00"},{"AppleTV":"11.00"},{"HBO Max":"0.00"},{"Nenhuma":"50.00"},{"Netflix":"9.00"},{"Outro":"20.00"}]},{"label":"tarde","data":[{"Amazon Prime":"0.00"},{"AppleTV":"0.00"},{"HBO Max":"5.00"},{"Nenhuma":"25.00"},{"Netflix":"20.00"},{"Outro":"50.00"}]}];

let tmp = array.reduce( function (p, c, i, a) {
   let label = c.label;
   if (!(label in p)) {
       p[label] = { };
   }
   for (let i = 0; i < c.data.length; i++) {
       let e = Object.entries(c.data[i]);
       let k = e[0][0];
       let v = parseFloat(e[0][1]);
       if (!(k in p[label])) {
           p[label][k] = [ ];
       }
       p[label][k].push(v);
   }
   return p;
}, { } );

let result = Object.entries(tmp).map( ( [label, _data] ) => (
  { label : label,
    data: Object.entries(_data).map( function ( [k, v] ) {
       let obj = { };
       obj[k] = ( v.reduce( (p,c) => p + c ) / v.length ).toFixed(2);
       return obj;
    } )
  }
) );

console.log(JSON.stringify(result, undefined, 2));

// Output:
// [
//   {
//     "label": "manha",
//     "data": [
//       { "Amazon Prime": "16.67" },
//       { "AppleTV": "0.00" },
//       { "HBO Max": "16.67" },
//       { "Nenhuma": "16.67" },
//       { "Netflix": "43.33" },
//       { "Outro": "6.67" }
//     ]
//   },
//   {
//     "label": "tarde",
//     "data": [
//       { "Amazon Prime": "5.00" },
//       { "AppleTV": "5.50" },
//       { "HBO Max": "2.50" },
//       { "Nenhuma": "37.50" },
//       { "Netflix": "14.50" },
//       { "Outro": "35.00" }
//     ]
//   }
// ]

Some final remarks on this answer compared to the question supplied.

  • I used toFixed(2) which implements a round up
    • Answers show "16.67" instead of "16.66" for the "manha" averages
  • For the "tarde" I got "37.50" ( i.e. (50 + 25) / 2 === 37.50 ) instead of "36.00"
  • All other values shown in the output matches to what is being requested in the question

Upvotes: 1

Max M
Max M

Reputation: 11

This is probably a bit longer then what you have hoped for but it should work.

let newAr = []
for(el of array) {
  const currentLabel = el.label
  let indexOfLabel = -1
  for(let i = 0; i < newAr.length; ++i) {
    if(newAr[i].label === currentLabel) indexOfLabel = i
  }
  if(indexOfLabel >= 0) {
    for(let i = 0; i < el.data.length; ++i) {
      const key = Object.keys(el.data[i])
      newAr[indexOfLabel].data[i][key] = parseInt(newAr[indexOfLabel].data[i][key])
      newAr[indexOfLabel].data[i][key] += parseInt(el.data[i][key])
    }
  } else {
    newAr.push(el)
  }
}

const occurrences = array.reduce(function (acc, curr) {
  return acc[curr.label] ? ++acc[curr.label] : acc[curr.label] = 1, acc
}, {});

for(el of newAr) {
  for(data of el.data) {
    data[Object.keys(data)] = (data[Object.keys(data)] / occurrences[el.label]).toFixed(2)
  }
}

Upvotes: 1

Related Questions