0

I want to write a rate system (similar to imdb movie rate), result maximum is 10, It's mean output can be a number between 0 to 10. and should show like this (2/10)

User can rate from, 1 to 5 (starts), each user allowed for once.

So consider, this array is all users rates.

let rate = ['4', '5', '1', '2', '5', '5', '2', '1', '3', '3', '4']; // all users rates
let result = 0;

for (i = 0; rate.length > i; i++) {
  result += parseInt(rate[i]);
}

let av = result / 10;

It's working good, but the problem is, this output never reach to big rate, like 9 or 10. So I should make a target, right?, my ceil target is 1250, it's mean if all users rate total is 1250, user should get rate 10/10.

let av = 1250 / 10;

But this return 125 ofcourse.

Target: show all user rate like this, 5/10 or 7/10 like imdb rate.

danny cavanagh
  • 115
  • 2
  • 8

3 Answers3

2

You need to get the right average and multiply with two, because of 10 (wanted) divided by 5 (your five star system) and get the average of a system with ten.

let rate = ['4', '5', '1', '2', '5', '5', '2', '1', '3', '3', '4']; // all users rates
let result = 0;

for (i = 0; rate.length > i; i++) {
    result += parseInt(rate[i], 10);
}

let av = result / rate.length;

console.log('average', av);
console.log((av * 2).toFixed(2), 'of 10');
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

Since you already divided by 2 the expected rating system 10 to 5 stars - you need to back multiply the stars by 2 to return to the 10 system.

// Convert 1-5 rating stars to 1-10 rating
const stars_to_ten = stars =>
   Math.round(stars.reduce((ten, star) => (ten += star * 2, ten), 0) / stars.length);

let result1 = stars_to_ten(['4', '5', '1', '2', '5', '5', '2', '1', '3', '3', '4']);
let result2 = stars_to_ten(['5', '5', '5', '5', '5']); 


console.log(result1); // 6
console.log(result2); // 10

IMDB weights its rating by other undisclosed factors - so perhaps it's not a mean or median of the votes cast, so a weighted algorithm can be still used.

Weighted rating by true Bayesian estimate

(WR) = (v ÷ (v+m)) × R + (m ÷ (v+m)) × C

Where:
R = average for the movie (mean) = (Rating)
v = number of votes for the movie = (votes)
m = minimum votes required to be listed in the Top 250 (currently 25000)
C = the mean vote across the whole report (currently 7.0)

const bayesianWeighted = (avg, totVotes, minVotesTop = 10, mean = 7.0) =>
  (avg * totVotes + mean * minVotesTop) / (totVotes + minVotesTop);
  
const calcRating = stars => {
  const avg = stars.reduce((a, v) => (a += +v * 2, a), 0) / stars.length;
  return Number(bayesianWeighted(avg, stars.length).toFixed(1));
};



// TEST
[
  [4, 5, 1, 2, 5, 5, 2, 1, 3, 3, 4], // 6.7
  [1, 1, 1, 1, 1],                   // 5.3
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],    // 4.5
  [5, 5, 5, 5, 5],                   // 8
  [5, 5, 5, 5, 5, 5, 5, 5, 5, 5],    // 8.5
].forEach(r => console.log(calcRating(r)));
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • Thanks, but please read my comments under Nina Scholz answer, your answer is too/ if two users rate 5 star, movie get 10/10 , makes no sense – danny cavanagh Feb 22 '20 at 11:46
  • @dannycavanagh So, the above is absolutely correct. On some pages, if you hover such voting system - sometimes in a tooltip you can see the relevant number of persons that voted i.e: `10/10 from 3 votes` or `7/10 from 13 votes`. Otherwise - you might want a specific **algorithm** in place you did not mentioned. – Roko C. Buljan Feb 22 '20 at 11:56
  • Maybe this algorithm https://stackoverflow.com/a/1411268/4540183 – danny cavanagh Feb 22 '20 at 11:59
  • @dannycavanagh maybe, who knows of today. That answer is from 2009. Do you have any specific algorithm in mind other than that one suggested? `rating = (R * v + C * m) / (v + m);` – Roko C. Buljan Feb 22 '20 at 12:07
  • No unfortunately like that, but As I said in comments, I think should set a ceil target, this means, if 200 number of rates be `200` and each of them rate five stars, so in total we got 1000, if total is 1000, output should be like this=> 10/10. or if total is 500, 5/10, I don't know this is a right logic or not. – danny cavanagh Feb 22 '20 at 12:12
  • 1
    @dannycavanagh Added an example, where the total rating of every movie in the database averages to `0.7` as the *mean*. Tweak the `minVotesTop` and `mean` as desired. – Roko C. Buljan Feb 22 '20 at 13:11
0

let rate = ['4', '5', '1', '2', '5', '5', '2', '1', '3', '3', '4']; // all users rates
let result = 0;

for (i = 0; rate.length > i; i++) {
  result += parseInt(rate[i]);
}

result = (result/rate.length)*2;
result = result.toFixed(2);

let rating = result+"/"+10;

console.log(rating);
  • `(result/rate.length)*2` == `result/rate.length*2` . Also, `result.toFixed(2)` will always add decimals, use 1 and `Number(result.toFixed(1))` to only have one decimal - when needed. Also `result+"/"+10` is less confusing and shorter when written as `result+"/10"` – Roko C. Buljan Feb 23 '20 at 17:29