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

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.

Related

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

How can I improve my code, and why does it not run?

This code is meant to take a number score from 0 to 100 and print the grade.
This is for school and is using a simplified version of javascript from the website 'codehs.com' I've been stuck on this for a while now, I would like help fixing my code.
/* This code is meant to take a number score from 0 to 100 and print
the grade. */
function start(){
/*given list */
lettergrade(100);
lettergrade(83);
lettergrade(68);
lettergrade(91);
lettergrade(47);
lettergrade(79);
}
/* this will print the grades above by using If/else if statements */
function lettergrade(score){
if(score = 90-100){
return("A");
}
else if(score = 80-90){
return("B");
}
else if(score = 70-79){
return("C");
}
else if(score = 60-69){
return("D");
}
else if(score = 0-59){
return("F");
}
}
The Expected result is to print the letter grades, A-F and the +/- sign if needed, but the code does not run.
There were multiple places wrong with your code:
lettergrade(100) is wrong because your function defined as letterGrade, there is case sensitivity in JS
if(score = 90-99) is wrong because if statement is expecting expression which you can choose to use == or ===. Single equal sign = meant for value assignment, not for comparison. And 90-99 is wrong, that is calculation not range. Correct way should be if(score >= 90 && score <= 99)
/* this will print the grades above by using If/else if statements */
function letterGrade(score){
if(score >= 90 && score <= 99){
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 if(score >= 0 && score < 60){
return("F");
}
}
function start(){
/*given list */
console.log(letterGrade(100));
console.log(letterGrade(83));
console.log(letterGrade(68));
console.log(letterGrade(91));
console.log(letterGrade(47));
console.log(letterGrade(79));
}
start();
The syntax is full of errors
Watch out for case sensitivity
You want an else statement to check if score is not valid
function letterGrade(score){
if(score >= 90 && score <= 100) {
return "A";
}
else if(score >= 80 && score < 90){
return "B";
}
else if(score >= 70 && score <= 79){
return "C";
}
else if(score >= 60 && score <= 69){
return "D";
}
else if(score >= 0 && score <= 59){
return "F";
}
else {
console.error(`Err: ${score} is not a valid score`);
}
}
Normal usage :
console.log(letterGrade(85)); // "B"
console.log(letterGrade(100)); // "A"
console.log(letterGrade(1)); // "F"
Handle errors :
console.log(letterGrade(4242)); // "Err: 4242 is not a valid Score"
console.log(letterGrade(-42)); // "Err: -42 is not a valid Score"
console.log(letterGrade("Hello World!")); // "Err: "Hello World!" is not a valid Score"

My code keeps returning undefined underneath the output

My JavaScript function keeps returning undefined underneath the correct output value.
let grade;
function getGrade(score) {
// Write your code here
if (score >= 25 && score <= 30) {
console.log('A');
}
else if (score >= 20 && score <= 25) {
console.log('B');
}
else if (score >= 15 && score <= 20) {
console.log('C');
}
else if (score >= 10 && score <= 15) {
console.log('D');
}
else if (score >= 5 && score <= 10) {
console.log('E');
}
else {
console.log('F');
}
return grade;
}
You haven't defined your grade. And it will always be undefined.
One way to do it is as follows:
function getGrade(score) {
var grade = "";
// Write your code here
if (score >= 25 && score <= 30) {
grade = "A";
}
else if (score >= 20 && score <= 25) {
grade = "B";
}
else if (score >= 15 && score <= 20) {
grade = "C";
}
else if (score >= 10 && score <= 15) {
grade = "D";
}
else if (score >= 5 && score <= 10) {
grade = "E";
}
else {
grade = "F";
}
return grade;
}
console.log(getGrade(27))
It seems you have return grade; at the bottom, but grade doesn't seem to be defined anywhere.
You should to set your variable "grade" value, or just delete
return grade;
Always check the console. It's currently singing at you, telling you grade is undefined.
You're trying to return something you haven't assigned a value to.
function getGrade(score) {
// ... //
return grade; //<-- nowhere do you define grade
}
Should be
function getGrade(score) {
let grade;
if (score >= 25 && score <= 30) grade = 'A';
else if (score >= 20 && score <= 25) grade = 'B';
else if (score >= 15 && score <= 20) grade = 'C';
else if (score >= 10 && score <= 15) grade = 'D';
else if (score >= 5 && score <= 10) grade = 'E';
else grade = 'F';
console.log(grade);
return grade;
}
Use return instead of console.log()
function getGrade(score) {
if (score >= 25 && score <= 30) {
return 'A'
}
else if (score >= 20 && score <= 25) {
return 'B'
}
else if (score >= 15 && score <= 20) {
return 'C';
}
else if (score >= 10 && score <= 15) {
return 'D';
}
else if (score >= 5 && score <= 10) {
return 'E';
}
else {
return 'F';
}
}
console.log(getGrade(20))
As there is difference of 5 b/w each grade range so you can use division and Math.floor
function getGrade(score) {
let grades = 'FEDCBA'
return score === 30 ? 'A' : grades[Math.floor((score)/5)]
}
console.log(getGrade(20))
console.log(getGrade(19))
console.log(getGrade(30))

Codewars: Grasshopper - Grade book challenge

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)}`));

Converting Score to Grade simpler. Is there a simpler way to write the following code?

It is somewhat tedious writing all the else if statements. Is there a simpler way to write the following code?
function convertScoreToGradeWithPlusAndMinus(score) {
// your code here
if(score <= 100 && score >= 98) return "A+";
else if(score <= 97 && score >= 93) return "A";
else if(score <= 92 && score >= 90) return "A-";
else if(score <= 89 && score >= 88) return "B+";
else if(score <= 87 && score >= 83) return "B";
else if(score <= 82 && score >= 80) return "B-";
else if(score <= 79 && score >= 78) return "C+";
else if(score <= 77 && score >= 73) return "C";
else if(score <= 72 && score >= 70) return "C-";
else if(score <= 69 && score >= 68) return "D+";
else if(score <= 67 && score >= 63) return "D";
else if(score <= 62 && score >= 60) return "D-";
else if(score <= 59 && score >= 0) return "F";
else return "INVALID SCORE";
}
var output = convertScoreToGradeWithPlusAndMinus(91);
console.log(output); // --> 'A-'
Shorter code is not better code always. You can write a very short version of this code by using ascii and some math tricks. But it will not be readable later by other ones. I think readablity and performance are two most important thing to consider.
var limits = ['-','','+','+']
function convertScoreToGradeWithPlusAndMinus(score) {
if(score==100) return 'A+';
if(score<59) return 'F';
var lCode = 74 - Math.floor(score/10);
var sign = limits[Math.floor((score % 10)/3)]
return String.fromCharCode(lCode)+ sign;
}
score : <input id="scoreBox" type="text"/>
<input onclick="alert(convertScoreToGradeWithPlusAndMinus(scoreBox.value))" value="calculate" type="button"/>
So the first thing to notice is that if a condition is "true", your function returns. So the "else if"s can be replaced by simple "if"s... The other thing is that since your ranges are continuous, you don't really need to test for a max and min each time:
function convertScoreToGradeWithPlusAndMinus(score) {
if(score > 100 || score < 0) return "INVALID SCORE";
if(score >= 98) return "A+";
if(score >= 93) return "A";
if(score >= 90) return "A-";
if(score >= 88) return "B+";
if(score >= 83) return "B";
if(score >= 80) return "B-";
if(score >= 78) return "C+";
if(score >= 73) return "C";
if(score >= 70) return "C-";
if(score >= 68) return "D+";
if(score >= 63) return "D";
if(score >= 60) return "D-";
return "F";
}
var output = convertScoreToGradeWithPlusAndMinus(91);
console.log(output); // --> 'A-'
I don't know if you count this as less tedious? Personally, my preferred way is to make a data structure mapping limits to values, end then simple loop on that until I find the right value.
function convertScoreToGradeWithPlusAndMinus(score) {
if(score > 100 || score < 0) return "INVALID SCORE";
var map = [
{max: 98, grade: "A+"},
{max: 93, grade: "A"},
{max: 90, grade: "A-"},
{max: 88, grade: "B+"},
{max: 83, grade: "B"},
{max: 80, grade: "B-"},
{max: 78, grade: "C+"},
{max: 73, grade: "C"},
{max: 70, grade: "C-"},
{max: 68, grade: "D+"},
{max: 63, grade: "D"},
{max: 60, grade: "D-"}
];
for(var loop = 0; loop < map.length; loop++) {
var data = map[loop];
if(score >= data.max) return data.grade;
}
return "F";
}
You still have the tedious job of defining your map though - I don't think you can avoid it in a case like this one.
Hope this helps!
Answered a while ago, but I encountered this question too. I think the following provides a nice middle way between the other two answers:
function convertScoreToGradeWithPlusAndMinus(score) {
if (score > 100 || score < 0) return "INVALID SCORE";
// sign logic
let sign = "";
if (score % 10 < 3) sign = "-";
if (score % 10 > 7 || score === 100) sign = "+";
// letter logic
if (score < 60) return "F";
if (score < 70) return "D" + sign;
if (score < 80) return "C" + sign;
if (score < 90) return "B" + sign;
return "A" + sign;
}

Categories

Resources