So assume I have the following array of 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 count = 0;
var avgScore = arr.reduce(function (sum,person) {
if (person.name == "John") {
count+=1;
return sum + parseFloat(person.score);
}
return sum;
},0)/count);
Question: Is there a way way to calculate the average score for "John" without creating a global count variable. Ideally, the count would be internal to the anonymous function in the arr.reduce.
reduce will return the only value (object or otherwise) if the list object only has one item, without calling iterator function.
The reduce() method executes the function for each value of the array (non-empty array) from left to right. The reduce() method has the following syntax: let arr = [];arr. reduce(callback(acc, curVal, index, src), initVal);
You could return an object with the average in it, calculated on every loop with an update.
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" }],
avgScore = arr.reduce(function (r, person) {
if (person.name === "John") {
r.sum += +person.score;
r.avg = r.sum / ++r.count;
}
return r;
}, { sum: 0, count: 0, avg: 0 }).avg;
console.log(avgScore);
A version with a closure and a direct return of the average.
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" }],
avgScore = arr.reduce(function (sum, count) {
return function (avg, person) {
if (person.name === "John") {
sum += +person.score;
return sum / ++count;
}
return avg;
};
}(0, 0), 0);
console.log(avgScore);
Above as ES6
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" }],
avgScore = arr.reduce(((sum, count) => (avg, person) => person.name === "John" ? (sum += +person.score) / ++count : avg)(0, 0), 0);
console.log(avgScore);
Here is yet another ES6 variant, which (ab)uses the third argument of reduce
as temporary storage, and calls reduce
again for a chained calculation of the average from the sum and count:
const 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"}
];
const avg = arr.reduce( ([sum, count], {name, score}, i) =>
(i = name == 'John', [sum + i * score, count + i]), [0, 0] )
.reduce( (sum, count) => sum/count );
console.log(avg);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With