I want to iterate though an array of different ratings, sort by each unique id, sum and calculate the average of each id’s ratings. Then save the averages in a new array, where I can call something like averageRating[i], where each entry will be each id’s rating.
The original array of objects looks like this, where id could be any number.
data = [{id: 1, rating: 1}, {id: 1, rating: 3}, {id: 1, rating: 1}, {id: 1, rating: 4}, {id: 1, rating}, {id: 2, rating: 3}, {id: 3, rating: 5}, {id: 1, rating: 5}, {id: 1, rating: 5}, {id: 1, rating: 5, {id: 1, rating: 1}, {id: 2, rating: 4}, {id: 1, rating: 3}, {id: 1, rating: 2}]
I was able to do this work it out with only one particular id, doing something like as follows, but having some trouble working out how to do with a dynamic number of ids.
var [average, updateAverage] = useState(0); let ratings = data.map((item) => item.rating); // Calculate average of the array let sum = ratings.reduce((a, b) => a + b, 0); let avg = sum / ratings.length || 0; let avgRounded = Math.round(avg); // Round to nearest whole number updateAverage = avgRounded;
Advertisement
Answer
This is probably over the top performant but you can actually do it in one loop if you enjoy a bit of maths. See this mathematical solution
const avgCount = {} // This is a temp var needed for accumulative averaging mathematical formula // THIS VAR HAS THE ANSWER IN const averages = data.reduce((averages, individualRating) => { // We need a counter of the number of times this ID has been seen already. This is needed by the algorithm. // Now we have seen a new rating with this id, increase the counter to reflect that. // If it's not there we start from 1. avgCount[individualRating.id] = (avgCount[individualRating.id] ?? 0) + 1 // Get the current rolling average. If there isn't one (this is first rating with this id we have seen), then its just the rating of this current item. const currAccumulatedAverage = averages[individualRating.id] ?? individualRating.rating // Calculate the new rolling average from the previous one averages[individualRating.id] = ((currAccumulatedAverage * (avgCount[individualRating.id] - 1)) + individualRating.rating) / avgCount[individualRating.id] return averages }, {})
This should be highly performant as there’s no multiple looping or intermediary structures.
For this input:
let data = [{id: 1, rating: 1}, {id: 1, rating: 3}, {id: 1, rating: 1}, {id: 1, rating: 4}, {id: 1, rating: 1}, {id: 2, rating: 3}, {id: 3, rating: 5}, {id: 1, rating: 5}, {id: 1, rating: 5}, {id: 1, rating: 5}, {id: 1, rating: 1}, {id: 2, rating: 4}, {id: 1, rating: 3}, {id: 1, rating: 2}]
It returns
{1: 2.8181818181818183, 2: 3.5, 3: 5}