A 2-pass works well without extra calculations, globals, or wrapper objects:
var arr = [
{"name": "John", "score": "8.8"},
{"name": "John", "score": "8.6"},
{"name": "John", "score": "9.0"},
{"name": "John", "score": "8.3"},
{"name": "Tom", "score": "7.9"}
];
var avgScore = arr.filter(x=>x.name=="John")
.reduce(function(v, n, c, r) {
return r.length-1 === c ?
(v + +n.score) / r.length :
v + +n.score;
},0);
console.log(avgScore);
If you are doing several different shapes, you should work in arrays of primitives so you can re-use methods:
var arr = [
{"name": "John", "score": "8.8"},
{"name": "John", "score": "8.6"},
{"name": "John", "score": "9.0"},
{"name": "John", "score": "8.3"},
{"name": "Tom", "score": "7.9"}
];
// define a few simple helpers
function pluck(o){ return o[this];}
function avg (v, n, c, r) { // calcs an average w/o a sum
return r.length-1 === c ?
(v + n) / r.length :
v + n ;
}
//now use the helpers to write succinct custom code:
var avgScore = arr.filter(x=>x.name=="John")
.map(pluck, "score")
.reduce(avg, 0);
console.log(avgScore);
The orig idea came from a custom report generator where users could pass in parameters and do some calcs on the backend without running custom code. the lib of generic methods such as avg can be used without a custom callback function. it's different, so i mention it...