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?'
}
Imtiaz Ahmed
  • 123
  • 3
  • 12
  • 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. – meriton Mar 30 '21 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? – Imtiaz Ahmed Mar 30 '21 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. – jarmod Mar 30 '21 at 16:27
  • Thanks for pointing that out. Can you please show a better solution? @jarmod – Imtiaz Ahmed Mar 30 '21 at 16:29
  • 1
    The `ceil` solution is better, imo. It's much simpler and a better generalization. – jarmod Mar 30 '21 at 16:31
  • Okay. so if our scores for each letter is not consistent then how will it work? for instance if 'A' has got 10 scores to evaluate to A, B has got 5 scores and so on. A = [31, 32, 33, 34, 35, 36, 37, 38, 39, 40] B = [26, 27, 28, 29, 30] C = [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] and so on – Imtiaz Ahmed Mar 30 '21 at 20:07

3 Answers3

2

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.

meriton
  • 68,356
  • 14
  • 108
  • 175
  • 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 – Imtiaz Ahmed Mar 30 '21 at 19:56
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));
DecPK
  • 24,537
  • 6
  • 26
  • 42
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
jarmod
  • 71,565
  • 16
  • 115
  • 122