How to sort letter grades in Javascript without a manual index

You could separate the grades and take an object for the postfix symbols.

const grades = ['B+', 'A', 'A-', 'A+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-', 'F']

grades.sort((a, b) => {
    const order = { '+': -1, '-': 1, undefined: 0 };
    return a[0].localeCompare(b[0]) || order[a[1]] - order[b[1]];
});

console.log(...grades);


Actually simpler than you think:

const grades = ['B+', 'F', 'A-', 'A+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-', 'A']


res = grades
    .map(x => x + ',')
    .sort()
    .map(x => x.slice(0, -1))

console.log(res.join())

The "magic" here is that , is right between + and - in the ascii table, so A becomes A, and sorts between A+ and A-.

As Nina suggested, you can also put the +, thing right into the sort callback:

const grades = ['B+', 'F', 'A-', 'A+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-', 'A']

let cmp = (x, y) => (x > y) - (x < y);

res = grades.sort((x, y) => cmp(x + ',', y + ','))

console.log(...res)

where cmp is the poor man's replacement for the <=> operator


If you want to just stick to ASCII values:

const grades = ['B+', 'F', 'A-', 'A+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-', 'A']

grades.sort((a, b) =>
  a.charCodeAt(0) === b.charCodeAt(0) // If the letters are the same
  ? (a.charCodeAt(1) || 44) - (b.charCodeAt(1) || 44) // Just compare the postfix
  : a.charCodeAt(0) - b.charCodeAt(0) // Otherwise compare the letters
);

console.log(...grades);

The ACII value of + is 43 and the ASCII value of - is 45, so we can use 44 (which is ,) when there is no postfix.

Alternatively, using template literals:

const grades = ['B+', 'F', 'A-', 'A+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-', 'A']

grades.sort((a, b) =>
  a.charCodeAt(0) === b.charCodeAt(0) 
  ? `${a},`.charCodeAt(1) - `${b},`.charCodeAt(1)
  : a.charCodeAt(0) - b.charCodeAt(0)
);

console.log(...grades);