Turning rating (number) into rating stars

Hello, let's say I have some testimonials and each testimonial has it's rating, (e.g. 3.5, 4, 2.5 etc.). So what I'm trying to build is if the rating is 3.5 for example it should display 3 full stars, one half star, and one empty star. Just to give you a better example of what I'm trying to build, I have two testimonials, one has rating of 4, and other one has rating of 4.5, so this is how they should display: If you'd like to play with it: https://codesandbox.io/s/ratings-zhlnhx?file=/src/App.js
CodeSandbox
ratings - CodeSandbox
ratings using react, react-dom, react-icons, react-scripts, sass
12 Replies
MarkBoots
MarkBoots•16mo ago
well, you can just use svg or a font icon that is full, half or empty for exmple ion-icon (there a more) ref: https://ionic.io/ionicons/usage
MarkBoots
MarkBoots•16mo ago
played with it a bit more, here a way with font-awesome in a single div, and css calculating the filled stars https://codepen.io/MarkBoots/pen/yLqWpmd
MarkBoots
MarkBoots•16mo ago
Tenkes
Tenkes•16mo ago
I was actually looking for Javascript, or rather mathematical solution to this. So it should see that rating is e.g. 3.5 and using (probably) some for loops and if statements it would display needed stars for it (3 full stars, one half star, and 1 empty stars). But I guess if I can't find better solution I can just put it all inside of one big object and work with that:
const RatingStars = {
0.5: [
<HalfStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />,
],
1: [
<FullStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />
],
1.5: [
<FullStar />,
<HalfStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />,
],
2: [
<FullStar />,
<FullStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />
],
2.5: [
<FullStar />,
<FullStar />,
<HalfStar />,
<EmptyStar />,
<EmptyStar />
],
3: [
<FullStar />,
<FullStar />,
<FullStar />,
<EmptyStar />,
<EmptyStar />
],
3.5: [
<FullStar />,
<FullStar />,
<FullStar />,
<HalfStar />,
<EmptyStar />
],
4: [
<FullStar />,
<FullStar />,
<FullStar />,
<FullStar />,
<EmptyStar />
],
4.5: [
<FullStar />,
<FullStar />,
<FullStar />,
<FullStar />,
<HalfStar />
],
5: [
<FullStar />,
<FullStar />,
<FullStar />,
<FullStar />,
<FullStar />
],
};
const RatingStars = {
0.5: [
<HalfStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />,
],
1: [
<FullStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />
],
1.5: [
<FullStar />,
<HalfStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />,
],
2: [
<FullStar />,
<FullStar />,
<EmptyStar />,
<EmptyStar />,
<EmptyStar />
],
2.5: [
<FullStar />,
<FullStar />,
<HalfStar />,
<EmptyStar />,
<EmptyStar />
],
3: [
<FullStar />,
<FullStar />,
<FullStar />,
<EmptyStar />,
<EmptyStar />
],
3.5: [
<FullStar />,
<FullStar />,
<FullStar />,
<HalfStar />,
<EmptyStar />
],
4: [
<FullStar />,
<FullStar />,
<FullStar />,
<FullStar />,
<EmptyStar />
],
4.5: [
<FullStar />,
<FullStar />,
<FullStar />,
<FullStar />,
<HalfStar />
],
5: [
<FullStar />,
<FullStar />,
<FullStar />,
<FullStar />,
<FullStar />
],
};
Coder_Carl
Coder_Carl•16mo ago
I would 2nd Marks' suggestion as that is much more performant BUT I would suggest that the elements be inputs rather than divs and use the steps to control the star rating. This will allow it to be accessible make certain that the input range has steps set to 10 i.e.
<label for="rating-1">
<span class="hidden">5 star</span>
</label>
<input type="range" max="100" min="0" step="10" aria-valuenow="" name="rating-1" id="rating-1">
<label for="rating-1">
<span class="hidden">5 star</span>
</label>
<input type="range" max="100" min="0" step="10" aria-valuenow="" name="rating-1" id="rating-1">
then programatically you can update the aria-valuenow to the appropriate star rating
MarkBoots
MarkBoots•16mo ago
I do agree with Coder_Carl and his additions But still want to show what an option could be incase of JS and instead of a big object You could make a little helper function that takes the score and amount of stars and returns the elements (strings in my case) Something like this (could be cleaner and more efficient probably, but I just woke up)
const ratingStars = (score, outof) => {
score = Math.round(score*2)/2; //to make sure it is rounded to nearest 0.5

const full = Math.floor(score);
const empty = Math.floor(outof - score);
const half = outof - full - empty;
const starsArray = [];

for(let i = 0; i < full ; i++) starsArray.push("fullStar")
for(let i = 0; i < half ; i++) starsArray.push("halfStar")
for(let i = 0; i < empty; i++) starsArray.push("emptyStar")

return starsArray;
}

console.log(ratingStars(4,5)) // ["fullStar","fullStar","fullStar","fullStar","emptyStar"]
console.log(ratingStars(1.5,5)) // ["fullStar","halfStar","emptyStar","emptyStar","emptyStar"]
console.log(ratingStars(6,10)) // [fullStar","fullStar","fullStar","fullStar","fullStar","fullStar","emptyStar","emptyStar","emptyStar","emptyStar"]
const ratingStars = (score, outof) => {
score = Math.round(score*2)/2; //to make sure it is rounded to nearest 0.5

const full = Math.floor(score);
const empty = Math.floor(outof - score);
const half = outof - full - empty;
const starsArray = [];

for(let i = 0; i < full ; i++) starsArray.push("fullStar")
for(let i = 0; i < half ; i++) starsArray.push("halfStar")
for(let i = 0; i < empty; i++) starsArray.push("emptyStar")

return starsArray;
}

console.log(ratingStars(4,5)) // ["fullStar","fullStar","fullStar","fullStar","emptyStar"]
console.log(ratingStars(1.5,5)) // ["fullStar","halfStar","emptyStar","emptyStar","emptyStar"]
console.log(ratingStars(6,10)) // [fullStar","fullStar","fullStar","fullStar","fullStar","fullStar","emptyStar","emptyStar","emptyStar","emptyStar"]
Tenkes
Tenkes•16mo ago
Yes! that is exactly what I needed, thank you! I also remembered ChatGPT exists so I asked him too, here's his solution if you'd like to see it:
const getStars = (rating) => {
let fullStars = Math.floor(rating);
let halfStars = rating % 1 >= 0.5 ? 1 : 0;
let emptyStars = 5 - fullStars - halfStars;

let stars = [];
for (let i = 0; i < fullStars; i++) {
stars.push("full");
}
if (halfStars) {
stars.push("half");
}
for (let i = 0; i < emptyStars; i++) {
stars.push("empty");
}
return stars;
};
const getStars = (rating) => {
let fullStars = Math.floor(rating);
let halfStars = rating % 1 >= 0.5 ? 1 : 0;
let emptyStars = 5 - fullStars - halfStars;

let stars = [];
for (let i = 0; i < fullStars; i++) {
stars.push("full");
}
if (halfStars) {
stars.push("half");
}
for (let i = 0; i < emptyStars; i++) {
stars.push("empty");
}
return stars;
};
Pretty similar to yours
MarkBoots
MarkBoots•16mo ago
Wow, chatGPT stole my work, or `maybe I am chatGPT 🤔 It still misses the check for 0.5 and it could be more generic with the amount of stars though
Tenkes
Tenkes•16mo ago
well somehow it works for 0.5 lol and I don't think there's need for amount of stars since it'll probably always be 5
CodeNascher
CodeNascher•16mo ago
Not sure, if still relevant, but here's my approach https://codepen.io/CodeNascher/pen/ZEjNJew
CodeNascher
CodeNascher•16mo ago
Might not be the best solution, rather a quick idea that worked well enough
Unknown User
Unknown User•16mo ago
Message Not Public
Sign In & Join Server To View