Convert values to percents based on two other values in array of objects



I have an array of objects:

let data = [
  {date:'2018-01-01', device: 'iphone',   site: 'google', val1:10, val2:20, val3:30},
  {date:'2018-01-01', device: 'iphone',   site: 'bing',   val1:23, val2:12, val3:14},
  {date:'2018-01-01', device: 'iphone',   site: 'jeeves', val1:67, val2:78, val3:12},
  {date:'2018-01-01', device: 'ipad',     site: 'google', val1:10, val2:20, val3:30},
  {date:'2018-01-01', device: 'ipad',     site: 'bing',   val1:23, val2:12, val3:14},
  {date:'2018-01-01', device: 'ipad',     site: 'jeeves', val1:67, val2:78, val3:12},
  {date:'2018-01-02', device: 'iphone',   site: 'google', val1:11, val2:22, val3:33},
  {date:'2018-01-02', device: 'iphone',   site: 'bing',   val1:25, val2:27, val3:28},
  {date:'2018-01-02', device: 'iphone',   site: 'jeeves', val1:67, val2:80, val3:15},
  {date:'2018-01-02', device: 'ipad',     site: 'google', val1:12, val2:21, val3:31},
  {date:'2018-01-02', device: 'ipad',     site: 'bing',   val1:26, val2:16, val3:11},
  {date:'2018-01-02', device: 'ipad',     site: 'jeeves', val1:65, val2:79, val3:55},
  {date:'2018-01-03', device: 'iphone',   site: 'google', val1:17, val2:19, val3:11},
  {date:'2018-01-03', device: 'iphone',   site: 'bing',   val1:13, val2:15, val3:12},
  {date:'2018-01-03', device: 'iphone',   site: 'jeeves', val1:69, val2:79, val3:15},
  {date:'2018-01-03', device: 'ipad',     site: 'google', val1:17, val2:51, val3:31},
  {date:'2018-01-03', device: 'ipad',     site: 'bing',   val1:25, val2:15, val3:17},
  {date:'2018-01-03', device: 'ipad',     site: 'jeeves', val1:61, val2:71, val3:15}
]

I want to convert val1 to percentages based on the date and device.

So on 2018-01-01 if the device is iphone I want the vals to be for example: val1 to be 10%, 23% & 67% respectively.

My code so far:

let data = [
  {date:'2018-01-01', device: 'iphone',   site: 'google', val1:10, val2:20, val3:30},
  {date:'2018-01-01', device: 'iphone',   site: 'bing',   val1:23, val2:12, val3:14},
  {date:'2018-01-01', device: 'iphone',   site: 'jeeves', val1:67, val2:78, val3:12},
  {date:'2018-01-01', device: 'ipad',     site: 'google', val1:10, val2:20, val3:30},
  {date:'2018-01-01', device: 'ipad',     site: 'bing',   val1:23, val2:12, val3:14},
  {date:'2018-01-01', device: 'ipad',     site: 'jeeves', val1:67, val2:78, val3:12},
  {date:'2018-01-02', device: 'iphone',   site: 'google', val1:11, val2:22, val3:33},
  {date:'2018-01-02', device: 'iphone',   site: 'bing',   val1:25, val2:27, val3:28},
  {date:'2018-01-02', device: 'iphone',   site: 'jeeves', val1:67, val2:80, val3:15},
  {date:'2018-01-02', device: 'ipad',     site: 'google', val1:12, val2:21, val3:31},
  {date:'2018-01-02', device: 'ipad',     site: 'bing',   val1:26, val2:16, val3:11},
  {date:'2018-01-02', device: 'ipad',     site: 'jeeves', val1:65, val2:79, val3:55},
  {date:'2018-01-03', device: 'iphone',   site: 'google', val1:17, val2:19, val3:11},
  {date:'2018-01-03', device: 'iphone',   site: 'bing',   val1:13, val2:15, val3:12},
  {date:'2018-01-03', device: 'iphone',   site: 'jeeves', val1:69, val2:79, val3:15},
  {date:'2018-01-03', device: 'ipad',     site: 'google', val1:17, val2:51, val3:31},
  {date:'2018-01-03', device: 'ipad',     site: 'bing',   val1:25, val2:15, val3:17},
  {date:'2018-01-03', device: 'ipad',     site: 'jeeves', val1:61, val2:71, val3:15}
]

//Grouping by date
const group_data = data.reduce((a, c) => {
                          (a[c['date']] = a[c['date']] || []).push(c);
                          return a;
                        }, {});
                    

// I am able to sum the data but not % each of them. Here I am able to group by 2 value, that is 'date' and 'device'
const grp_data = (grouped_data, first_group, second_group) => {
    for (const key of Object.keys(grouped_data)) {
        const map_data = new Map();
        for (const {
            [first_group]: date, [second_group]: device, ...other_vals
        }
        of grouped_data[key]) {
            const row = map_data.get(date + ',' + device) || {
                date,
                device
            }
            Object.entries(other_vals).forEach(([k, v]) => {
                if (/^[0-9,.]*$/.test(v) === true && typeof v !== 'number') v = parseFloat(v.replace(/,/gi, ''));
                row[k] = (row[k] || 0) + v
            });
            map_data.set(date + ',' + device, row);
        }
        grouped_data[key] = [...map_data.values()];
    }
}
grp_data(group_data, 'date', 'device');

console.log(group_data);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

While I am able to group the data by 2 keys, I am not sure how to convert the val1’s to %’s.

I am not sure how to go from here to get this as the data:

{date:'2018-01-01', device: 'iphone',   site: 'google', val1:10%, val2:18.18%, val3:53.57%},
{date:'2018-01-01', device: 'iphone',   site: 'bing',   val1:23%, val2:10.90%, val3:25%},
{date:'2018-01-01', device: 'iphone',   site: 'jeeves', val1:67%, val2:70.90%, val3:21.42%},
...

Answer

As per your explanation, I tried this in completely different way.

let data = [
  {date:'2018-01-01', device: 'iphone',   site: 'google', val1:10, val2:20, val3:30},
  {date:'2018-01-01', device: 'iphone',   site: 'bing',   val1:23, val2:12, val3:14},
  {date:'2018-01-01', device: 'iphone',   site: 'jeeves', val1:67, val2:78, val3:12},
  {date:'2018-01-01', device: 'ipad',     site: 'google', val1:10, val2:20, val3:30},
  {date:'2018-01-01', device: 'ipad',     site: 'bing',   val1:23, val2:12, val3:14},
  {date:'2018-01-01', device: 'ipad',     site: 'jeeves', val1:67, val2:78, val3:12},
  {date:'2018-01-02', device: 'iphone',   site: 'google', val1:11, val2:22, val3:33},
  {date:'2018-01-02', device: 'iphone',   site: 'bing',   val1:25, val2:27, val3:28},
  {date:'2018-01-02', device: 'iphone',   site: 'jeeves', val1:67, val2:80, val3:15},
  {date:'2018-01-02', device: 'ipad',     site: 'google', val1:12, val2:21, val3:31},
  {date:'2018-01-02', device: 'ipad',     site: 'bing',   val1:26, val2:16, val3:11},
  {date:'2018-01-02', device: 'ipad',     site: 'jeeves', val1:65, val2:79, val3:55},
  {date:'2018-01-03', device: 'iphone',   site: 'google', val1:17, val2:19, val3:11},
  {date:'2018-01-03', device: 'iphone',   site: 'bing',   val1:13, val2:15, val3:12},
  {date:'2018-01-03', device: 'iphone',   site: 'jeeves', val1:69, val2:79, val3:15},
  {date:'2018-01-03', device: 'ipad',     site: 'google', val1:17, val2:51, val3:31},
  {date:'2018-01-03', device: 'ipad',     site: 'bing',   val1:25, val2:15, val3:17},
  {date:'2018-01-03', device: 'ipad',     site: 'jeeves', val1:61, val2:71, val3:15}
]

let dataObj = {}; 
data.forEach(d => {
    if(!dataObj[d.date]) dataObj[d.date] = {};
    if(!dataObj[d.date][d.device]) dataObj[d.date][d.device] = {};
    if(!dataObj[d.date][d.device].val1) dataObj[d.date][d.device].val1 = 0;
    if(!dataObj[d.date][d.device].val2) dataObj[d.date][d.device].val2 = 0;
    if(!dataObj[d.date][d.device].val3) dataObj[d.date][d.device].val3 = 0;
    dataObj[d.date][d.device].val1 += d.val1;
    dataObj[d.date][d.device].val2 += d.val2;
    dataObj[d.date][d.device].val3 += d.val3;
});

data.forEach(d => {
  d.val1 = d.val1 * 100 / dataObj[d.date][d.device].val1;
  d.val2 = d.val2 * 100 / dataObj[d.date][d.device].val2;
  d.val3 = d.val3 * 100 / dataObj[d.date][d.device].val3;
});

console.log(data);


Source: stackoverflow