0

I've written a a function which takes score as parameter and should return the letter grade. There are some conditions to be followed while writing the code. ie: return 'A' if 25 < score <= 30 return 'B' if 20 < score <= 25 and so on. So I wanted to do this by omitting a whole lot of if-else's. As I'm new to javascript this is all I could come up with:

// This function takes Nested arrays and a single number, 
// which checks its availability in 
// the inside array and then return the index of the array
function my_index(arr, score) {
    for (const [index, elem] of arr.entries()) {
        if (elem.includes(score)) {
            return index;
        }
        
    }
}

// function to get letter grade
function getGrade(score) {
    let grade;
    var gradeDict = {
        'A': [26, 27, 28, 29, 30],
        'B': [21, 22, 23, 24, 25],
        'C': [16, 17, 18, 19, 20],
        'D': [11, 12, 13, 14, 15],
        'E': [6, 7, 8, 9, 10],
        'F': [0, 1, 2, 3, 4, 5] 
    }
    var keys = Object.keys(gradeDict);
    var values = [Object.values(gradeDict)]
    grade = keys[my_index(values, score)]
        
    return grade;
}

The first function works fine. It returns the index of nested array. But the main function getGrade happens to return 'Undefined'. Can't think of a better solution than this to reduce a bunch of ugly if-elses.

var question = {
    '1st': 'Can anybody help me get this done?',
    '2nd': 'Is there any better way to do this?'
}
6
  • 2
    Try value = Object.values(gradeDict) rather than value = [Object.values(gradeDict)]. The first creates an array like [[[26, 27 28, 29, 30], ...]], the second an array lke [[26, 27, 28, 29, 30], ...], which is more in line with what my_index expects. Commented Mar 30, 2021 at 16:07
  • As I designed 'my_index' for nested list, I thought I should make it a list of list in 'getGrade'. It's working now, Thank you so much. And is there any better way to accomplish this other than this one? Commented Mar 30, 2021 at 16:15
  • 1
    Any solution that has [26, 27, 28, 29, 30] etc. is not a good solution imho. These are ranges of consecutive integers so there is no need to store them as lists. One way to realize that this is a bad solution is to increase the size of the problem - let's say there are 26 grades (A-Z) or 1 million grades. How would your solution using lists look in that case? That's your clue that a list-based solution is a poor choice. Commented Mar 30, 2021 at 16:27
  • Thanks for pointing that out. Can you please show a better solution? @jarmod Commented Mar 30, 2021 at 16:29
  • 1
    The ceil solution is better, imo. It's much simpler and a better generalization. Commented Mar 30, 2021 at 16:31

3 Answers 3

3

Is there a better way to write this?

I'd do:

function getLetterGrade(score) {
  return ['F', 'F', 'E', 'D', 'C', 'B', 'A'][Math.ceil(score / 5)];
}

(F occurs twice because more scores map to F than to other grades)

It may be a bit cryptic, but is easier to tune should the possible score ever change.

Sign up to request clarification or add additional context in comments.

1 Comment

in our current situation score for each grade are consistent with 5 numbers except for 'F'. so we can divide the score with 5. But if our scores for each grade are not consistent ie: 5, 6 and so on. what will be your solution in the Math.ceil approach? @meriton
1

Remove the outer [] array of the Object.values. Object.values already returns values in array.

from

var values = [Object.values(gradeDict)];

to

var values = Object.values(gradeDict);

working example:

function my_index(arr, score) {
  for (const [index, elem] of arr.entries()) {
    if (elem.includes(score)) {
      return index;
    }
  }
}

function getGrade(score) {
  let grade;
  var gradeDict = {
    A: [26, 27, 28, 29, 30],
    B: [21, 22, 23, 24, 25],
    C: [16, 17, 18, 19, 20],
    D: [11, 12, 13, 14, 15],
    E: [6, 7, 8, 9, 10],
    F: [0, 1, 2, 3, 4, 5],
  };
  var keys = Object.keys(gradeDict);
  var values = Object.values(gradeDict);
  grade = keys[my_index(values, score)];

  return grade;
}

console.log(getGrade(5));
console.log(getGrade(25));

Alternate solution

function getGrade(score) {
  let grade;
  var gradeDict = {
    A: [26, 27, 28, 29, 30],
    B: [21, 22, 23, 24, 25],
    C: [16, 17, 18, 19, 20],
    D: [11, 12, 13, 14, 15],
    E: [6, 7, 8, 9, 10],
    F: [0, 1, 2, 3, 4, 5],
  };

  for (let key in gradeDict) {
    if (gradeDict[key].includes(score)) return key;
  }

  return "Not found";
}

console.log(getGrade(5));
console.log(getGrade(25));

4 Comments

Thanks, Man, As @meriton suggested. It's working this way
@ImtiazAhmed alternate solution included
You can also search element in array using find, filter also.
Thanks man! I appreciate it. The solution omitting the my_index method is awesome.
1

I like the ceil solution proposed earlier, but here is another general solution in case it's helpful:

function grade(score) {
  if (score < 0 || score > 30) throw RangeError(`Score ${score} out of range`);

  for (let ii = 5; ii >= 0; ii--) {
    if (score > 5*ii) return String.fromCharCode(70 - ii);
  }

  return 'F';
}

console.log(0, '=>', grade(0))   // F
console.log(4, '=>', grade(4))   // F
console.log(6, '=>', grade(6))   // E
console.log(10, '=>', grade(10)) // E
console.log(27, '=>', grade(27)) // A
console.log(30, '=>', grade(30)) // A

1 Comment

While trying another approach I was actually struggling with the condition '0 > score > 30'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.