Calculate average of array of objects per key value using reduce I want to find average of an array of objects based on their key values using the new functional programming style. I found my way around array reduce and solved my problem, but not sure if this is the best way to do it. 

Please take a look at my code and see if this is the way to use reduce for my purpose. 

Let's say I have an array of objects as follows:

private data = [
    {tv: 1, radio:5,fridge:4},
    {tv: 2, radio:2,fridge:null},
    {tv: 3, radio:6,fridge:5}
];


I want to create another array containing the averages of each of the items in my data array. What I have, and is working, is below:

function summary(){
    var keys= Object.keys(data[0]);
    var sums = {};
    var averages = Object.keys(this.data.reduce((previous, element) =&gt; {
        keys.forEach(el =&gt; {
            if(element[el] !== null){
                if (previous.hasOwnProperty(el)) {
                    previous[el].value += element[el];
                    previous[el].count += 1;
                } else {
                    previous[el] = {
                        value: element[el],
                        count: 1
                    };
                }
            }
        });
        return previous;
    }, sums)).map(name =&gt; {
        return {
            name: name,
            average: sums[name].value / sums[name].count
        };
    });
    console.log(averages);
}


Running the code will give me my expected results:

average = [ 
    { "name": "tv", "average": 2 },
    { "name": "radio", "average": 4.333333333333333 }, 
    { "name": "fridge", "average": 4.5 } 
]


But is this the best way to solve my problem using new reduce functions?
Here is possibly an even more functional programming style solution, which makes use of a temporary ES6 Map object. This has the advantage over a plain object: you can turn it into an array of pairs, and chain on that to get the final result:



var data = [
    {tv: 1, radio:5, fridge:4},
    {tv: 2, radio:2, fridge:null},
    {tv: 3, radio:6, fridge:5}
];

var avg = Array.from(data.reduce(
        (acc, obj) =&gt; Object.keys(obj).reduce( 
            (acc, key) =&gt; typeof obj[key] == "number"
                ? acc.set(key, (acc.get(key) || []).concat(obj[key]))
                : acc,
        acc),
    new Map()), 
        ([name, values]) =&gt;
            ({ name, average: values.reduce( (a,b) =&gt; a+b ) / values.length })
    );

console.log(avg);




Instead of immediately summing up the values, this code first collects the different values into an array per property, in a Map, then it calculates the averages from those arrays, turning it into the desired target structure.

Alternative output structure

Personally I find it more logical to produce output that has the same structure as the input objects, so I provide this very similar alternative. Only the final map is replaced by a reduce:



var data = [
    {tv: 1, radio:5, fridge:4},
    {tv: 2, radio:2, fridge:null},
    {tv: 3, radio:6, fridge:5}
];

var avg = Array.from(data.reduce(
        (acc, obj) =&gt; Object.keys(obj).reduce( 
            (acc, key) =&gt; typeof obj[key] == "number"
                ? acc.set(key, (acc.get(key) || []).concat(obj[key]))
                : acc,
        acc),
    new Map())).reduce( 
        (acc, [name, values]) =&gt;
            Object.assign(acc, { [name]: values.reduce( (a,b) =&gt; a+b ) / values.length }),
        {}
    );

console.log(avg);




Performance improvement

As you asked in comments about performance, I tried to improve on it, without giving up on functional programming.

I took my first code version (which will be more performant than the second), and changed the first half of the algorithm: the numbers are now summed up immediately, keeping a count next to it. For this I introduced an immediately invoked (arrow) function:



var data = [
    {tv: 1, radio:5, fridge:4},
    {tv: 2, radio:2, fridge:null},
    {tv: 3, radio:6, fridge:5}
];

var avg = Array.from(data.reduce(
        (acc, obj) =&gt; Object.keys(obj).reduce( 
            (acc, key) =&gt; typeof obj[key] == "number"
                ? acc.set(key, ( // immediately invoked function:
                        ([sum, count]) =&gt; [sum+obj[key], count+1] 
                    )(acc.get(key) || [0, 0])) // pass previous value
                : acc,
        acc),
    new Map()), 
        ([name, [sum, count]]) =&gt; ({ name, average: sum/count })
    );

console.log(avg);




This stays within the functional programming rules, but I expect better performance than the first two versions I posted.