Codewars: Grasshopper - Grade book challenge - javascript

This is one of those times where the solution is staring me right in the face but I can't seem to find it! So please be patient with me. The kata instruction is the following:
Complete the function so that it finds the mean of the three scores passed to it and returns the letter value associated with that grade.
Numerical Score Letter Grade
90 <= score <= 100 'A'
80 <= score < 90 'B'
70 <= score < 80 'C'
60 <= score < 70 'D'
0 <= score < 60 'F'
Tested values are all between 0 and 100. There is no need to check for negative values or values greater than 100.
Here is my solution:
function getGrade (s1, s2, s3) {
var score = (s1 + s2 + s3) / 3;
if (90 <= score && score >= 100) {
return 'A';
} else if (80 <= score && score > 90) {
return 'B';
} else if (70 <= score && score > 80) {
return 'C';
} else if (60 <= score && score > 70) {
return 'D';
} else if (0 <= score && score > 60) {
return 'F';
}
}
getGrade(5,40,93);
getGrade(30,85,96);
getGrade(92,70,40);
Can't for the life of me figure out what I am doing wrong.

Your conditions in if statement are all wrong. These are the right conditions
function getGrade (s1, s2, s3) {
var score = (s1 + s2 + s3) / 3;
if (score >= 90 && score <= 100) {
return 'A';
} else if (score >= 80 && score < 90) {
return 'B';
} else if (score >= 70&& score < 80) {
return 'C';
} else if (score >= 60 && score < 70) {
return 'D';
} else {
return 'F';
}
}

your conditions are wrong and you don't need multiple check in same if .Change your code to this:
function getGrade (s1, s2, s3) {
var score = (s1 + s2 + s3) / 3;
if (score >= 90 && score <= 100) {
return 'A';
} else if (score >= 80 ) {
return 'B';
} else if (score >= 70 ) {
return 'C';
} else if (score >= 60) {
return 'D';
} else{
return 'F';
}
}
console.log(getGrade(5,40,93));
console.log(getGrade(30,85,96));
console.log(getGrade(92,70,40));

You could use only if clauses without else parts and check only the lower bound, because you have already checked the upper bound.
The check for upper 100 is missing, because your given range is between 0 and 100.
function getGrade(s1, s2, s3) {
var score = (s1 + s2 + s3) / 3;
if (score >= 90) {
return 'A';
}
if (score >= 80) {
return 'B';
}
if (score >= 70) {
return 'C';
}
if (score >= 60) {
return 'D';
}
return 'F';
}
console.log(getGrade(5, 40, 93));
console.log(getGrade(30, 85, 96));
console.log(getGrade(92, 70, 40));

Whenever you find yourself writing long chains of if-else statements, see if you can find a pattern and use a lookup table. Here, we have only 5 grade buckets, but what if we had 20 or 100? You can see the if-else approach isn't scalable.
In this case, if we use the string "FFFFFFDCBAA" then we've enumerated all 5 grade buckets in a way that lets us index into after dividing the score by 10. The code for that would be: "FFFFFFDCBAA"[score/10|0] where | 0 is the floor operation, chopping off the decimal. The extra "A" handles the case of 100.
Secondly, the arguments to the function (s1, s2, s3) make no sense. Why 3 scores? If we have 4 score, or 20 scores, the function can't be used and we have to rewrite the whole function with the right number of arguments (and the 20-argument one will be pretty ugly). I realize this header is what the kata author gave you, but there's no reason we can't make it handle any number of arguments using (...args) and still pass the tests. If we take the average of the arguments using args.reduce((a, e) => a + e, 0) / args.length, we're left with the following solution:
const sum = a => a.reduce((a, e) => a + e, 0);
const avg = a => sum(a) / a.length;
const getGrade = (...args) => "FFFFFFDCBAA"[avg(args)/10|0];
[
[0],
[0, 100, 50],
[90, 95, 100],
[80, 60],
[81, 79],
[80, 59],
].forEach(test => console.log(`${test} => ${getGrade(...test)}`));

Related

Different result of an if else on Javascript [duplicate]

This question already has answers here:
What's the prettiest way to compare one value against multiple values? [duplicate]
(8 answers)
Closed 1 year ago.
I don't know why I keep getting the console.log from if statement even both averages are above 100.
Where did I put my mistake?
const dolphinsScores = 97 + 112 + 101;
const dolphinsAverage = dolphinsScores / 3;
const koalasScores = 109 + 95 + 123;
const koalasAverage = koalasScores / 3;
console.log(dolphinsAverage);
console.log(koalasAverage);
if (dolphinsAverage || koalasAverage < 100) {
console.log('One of the team is already lost')
} else if (dolphinsAverage > koalasAverage) {
console.log('Team Dolphins are the winner of the competition! 🐬🎉');
} else if (koalasAverage > dolphinsAverage) {
console.log('Team Koalas are the winner of the competition! 🐨🎉');
}
this statement
if (dolphinsAverage || koalasAverage < 100)
resolve as
if (true || koalasAverage < 100)
what you need is
if (dolphinsAverage < 100 || koalasAverage < 100)
Your if condition was checking if there is any value in dolphinsAverage which will always be true if there is any value in dolphinsAverage. So try this if you want to know if dolphinsAverage's value is less than 100.
if (dolphinsAverage < 100 || koalasAverage < 100) {
console.log('One of the team is already lost')
} else if (dolphinsAverage > koalasAverage) {
console.log('Team Dolphins are the winner of the competition! 🐬🎉');
} else if (koalasAverage > dolphinsAverage) {
console.log('Team Koalas are the winner of the competition! 🐨🎉');
}
const dolphinsScores = 97 + 112 + 101;
const dolphinsAverage = dolphinsScores / 3;
const koalasScores = 109 + 95 + 123;
const koalasAverage = koalasScores / 3;
console.log(dolphinsAverage);
console.log(koalasAverage);
if ((dolphinsAverage < 100) || (koalasAverage < 100)) {
console.log('One of the team is already lost')
} else if (dolphinsAverage > koalasAverage) {
console.log('Team Dolphins are the winner of the competition! 🐬🎉');
} else if (koalasAverage > dolphinsAverage) {
console.log('Team Koalas are the winner of the competition! 🐨🎉');
}
your if statement should be
if (dolphinsAverage < 100 || koalasAverage < 100) {

Grading Students in JS with recursion - Range Error

I was trying to work on this hackerrank problem.
Every student receives a grade in the inclusive range from
0-100 to .
Any less than 38 is a failing grade.
Sam is a professor at the university and likes to round each student's according to these rules:
If the difference between grade the and the next multiple of
5 is less than 3, round up to the next multiple of 5. If the value of grade is less than 38, no rounding occurs as the
result will still be a failing grade.
Given the initial value of for each of Sam's students, write code to
automate the rounding process.
My code is:
function gradingStudents(grades) {
const roundup = y => y + 1;
{
if ( grades < 38 || grades % 5 === 0) return grades;
else if ( grades % 5 < 4 && grades % 5 !== 0) return roundup(grades);
}
{
if (roundup % 5 === 0) return roundup;
else { gradingStudents(roundup + 1) }
}
}
gradingStudents(38) // -> 39
I tried to use Math.ceil(grades) inside the variable roundup but output didnt change. So, when you invoke the function with a number that is not before a multiple of 5 (e.g. 43) it returns the proceeding number. However, if it is the number before a multiple of 5 it gives a range error. "maximum call stack size reached."
As far as I got, the code doesnt proceed to the second part. Even if it did, I am not sure if it would fetch the current value of the function roundup when dealing with if statements in the second block.
What do I dont get in here?
Also, this is actually meant for an array output but since I am a beginner I am pretty much okay with this one for the start as well :D .
Javascript solution:
function gradingStudents(grades) {
return grades.map((grade) => {
if (grade > 37) {
const offset = 5 - (grade % 5);
if (offset < 3) {
grade += offset;
}
}
return grade;
});
}
Try this:
function gradingStudents(grades) { //input: 43
var finalGrade;
if(grade < 38)
return grades;
else{
var gradeDif = grades % 5; //3
if(gradeDif > 3){
return grades;
}
else {
return grades + (5 - gradeDif); //Output: 45
}
}
}
One solution calculates the next multiple of 5 no bigger than the grade and uses that value to test whether or not to round up (next5 - grade < 3).
We write a simple function to round an individual grade and then for a list of grades use .map with that function.
const roundGrade = (grade) => {
const next5 = 5 * Math.ceil (grade / 5)
return (grade < 38) ? grade : (next5 - grade < 3) ? next5 : grade
}
const gradingStudents = (grades) =>
grades .map (roundGrade)
console .log (gradingStudents ([73, 67, 38, 33]))
Note that, like most solutions to this problem, no recursion is needed.
1-Use the Array.prototypt.map according to the question logic.
const gradingStudents = (grades) => grades
.map(n => (n >= 38 && n % 5 >= 3)?(n + ( 5 - ( n % 5 ) )):n )
let result = gradingStudents([0,25,38,56,89,77,78,57,58])
console.log(result)
My solution is this
function gradingStudents(grades) {
grades.map((grade,i)=>{
if(grade >= 38){
let fg = (grade/5).toString().split('.');
if(fg[1]>5){
grades[i]=((parseInt(fg[0],10)+1) * 5);
};
}
});
return grades;
}
console.log(gradingStudents([73,67,38,33]));
my solution:
function gradingStudents(grades) {
return grades.map(function(grade) {
return (grade >= 38 && grade % 5 >= 3) ? grade + 5 - (grade % 5) : grade;
});
}

Recursively setting a value depending on range using JavaScript

I don't know how to word this but this is what I'm trying to do:
if (score >= 0 && score <= 10) overallScore = 0;
else if (score >= 11 && score <= 20) overallScore = 1;
else if (score >= 21 && score <= 30) overallScore = 2;
else if (score >= 31 && score <= 40) overallScore = 3;
else if (score >= 91 && score <= 100) overallScore = 9;
...
Is there any way to recursively do this using a function?
overallScore = Math.max(0, Math.floor((score - 1) / 10));
no need for recursion. But if you need that:
const getOverall = score => score <= 10 ? 0 : getOverall(score - 10) + 1;
Recursion is not really appropriate here, since you can get the required value in constant time. Recursion becomes interesting when you need at least O(logn) time.
But as you ask for it, here is one way to make it recursive:
function range(score, depth = 0) {
return score <= 10 || depth >= 9 ? 0 : range(score-10, depth+1) + 1;
}
console.log(range(0)); // 0
console.log(range(10)); // 0
console.log(range(11)); // 1
console.log(range(60)); // 5
console.log(range(91)); // 9
console.log(range(110)); // 9

I am doing codewars and i am stuck on the students final grade challenge

I am not sure what i am doing wrong, it all works but something is off and i cant catch it since i am a newbie, any ideas?
function finalGrade(exam, projects) {
if (exam >= 90 || projects > 10) {
return 100;
} else if (exam >= 75 && projects === 5) {
return 90;
} else if (exam >= 50 && projects === 2) {
return 75;
} else {
return 0;
}
}
Question - This function should take two arguments: exam - grade for exam (from 0 to 100); projects - number of completed projects (from 0 and above);
This function should return a number (final grade). There are four types of final grades:
100, if a grade for the exam is more than 90 or if a number of completed projects more than 10.
90, if a grade for the exam is more than 75 and if a number of completed projects is minimum 5.
75, if a grade for the exam is more than 50 and if a number of completed projects is minimum 2.
0, in other cases
You are using === in last to if statements. You should use >= if you want to check minimum number of projects
function finalGrade(exam, projects) {
if (exam > 90 && projects >= 10) {
return 100;
} else if (exam > 75 && projects >= 5) {
return 90;
} else if (exam > 50 && projects >= 2) {
return 75;
} else {
return 0;
}
}
Just only one part, the structure
if () {
return
} else if () {
return
} else {
return
}
can be simplified to omit else parts and just go on with if
if () {
return
}
if () { // as many more
return
}
return
Inside the all if condition the grade for exam is more than 90, 75, and 50, so it will be just grade>90, grade>75 and grade>50 and not grade>=90, grade>=75 and grade>=50. And another thing is that you should use >= instead of === .
Here's my solution:-
function finalGrade (exam, projects) {
if(exam > 90 || projects > 10)
return 100;
if(exam > 75 & projects >= 5)
return 90;
if(exam > 50 & projects >= 2)
return 75;
return 0;
}

Which style of code is better: being efficient or separating concerns?

I wonder if this might come down to personal taste or if there is a generally agreed upon answer to this. I've got a piece of code that could be written in one of two ways and though I think it's something of a trivial example in terms of efficiency, I'd like to know what the generally accepted answer is for future extrapolations.
Here's the code I currently have, essentially a score is passed and some text is updated accordingly. The colour of the text is also changed by the score value.
function getBSTotalText(score) {
var scoreText;
if (score >= 0 && score <= 12) {
scoreText = "0 - 12 HIGH RISK";
}
else if (score >= 13 && score <= 14) {
scoreText = "13 - 14 MODERATE RISK";
}
else if (score >= 15 && score <= 16) {
scoreText = "15 - 16 LOW RISK";
}
else if (score >= 16) {
scoreText = "16+ NO RISK";
}
else {
scoreText = "";
}
return scoreText;
}
function getBSTotalColour(score) {
var colour;
if (score >= 0 && score <= 12) {
colour = "red";
}
else if (score >= 13 && score <= 14) {
colour = "amber";
}
else if (score >= 15 && score <= 16) {
colour = "yellow";
}
else if (score >= 16) {
colour = "grey";
}
else {
colour = "white";
}
return colour;
}
Now I could easily refactor this into one function and just get it to return an array or object to save basically copying and pasting the same code into a distinct function which from my understanding would conform to DRY but then break SOLID. Would best practice be to keep these functions distinct or merge them into one?
In this example, I'd say there's a compelling reason to refactor to a single function as both functions are concerned with the same thing - getting some formatted text.
function getBSTotalDisplayInfo(score) {
var result = {};
if (score >= 0 && score <= 12) {
result.colour = "red";
result.scoreText = "0 - 12 HIGH RISK";
}
else if (score >= 13 && score <= 14) {
result.colour = "amber";
result.scoreText = "13 - 14 MODERATE RISK";
}
else if (score >= 15 && score <= 16) {
result.colour = "yellow";
result.scoreText = "15 - 16 LOW RISK";
}
else if (score >= 16) {
result.colour = "grey";
result.scoreText = "16+ NO RISK";
}
else {
result.colour = "white";
result.scoreText = "";
}
return result;
}
Check what part of the code is repeated and move that into an extra function. In your case it's actually quite easy:
function getBSTotal(score) {
// returns some kind of enum
if (score >= 0 && score <= 12)
return 0;
else if (score >= 13 && score <= 14)
return 1;
else if (score >= 15 && score <= 16)
return 2;
else if (score >= 16)
return 4;
else
return 5;
}
function getBSTotalText(score) {
// now map the enum either to a text
return ["0 - 12 HIGH RISK",
"13 - 14 MODERATE RISK",
"15 - 16 LOW RISK",
"16+ NO RISK"
][getBSTotal(score)] || "";
}
function getBSTotalColour(score) {
// … or map it to a color
return ["red",
"amber"
"yellow",
"grey",
"white"
][getBSTotal(score)];
}
You still can make it more efficient by evaluating getBSTotal(score) only once and passing that to the mapping functions instead of score.

Categories

Resources