How to Automatically Calculate Letter Grades, But Every Solution Is Bad

How to Automatically Calculate Letter Grades, but Every Solution Is Bad Featured Image

Let’s say you have your grade as a percentage. Would you be able to write a code segment that could convert that percentage to a letter grade? That’s a question that blew up recently on Twitter, and some of the solutions are hilarious.

Table of Contents

Leaning Into a Meme

For as long as YouTube has been around, there has been this fun video format where you take a normal title and follow it up with “but.” The one that always comes to mind for me is “shrek but every time he takes a STEP it gets 5% faster.”

No doubt this is a hilarious video concept, which is why there are literally thousands of these. Most often I see this with songs, where there is some ridiculous premise repeated. For instance, here’s only the bridges of Taylor Swift’s album, Lover:

Well recently, there was a bit of discourse on Twitter about how to solve a certain programming problem, and this meme format showed up again. Needless to say, this particular format is the title of this article.

Calculating Letter Grades

A very common early programming problem we give computer science students is one where have them compute letter grades from percentages. This is a great early programming problem because it gets students acquainted with the syntax of basic branching. If they can construct a sequence of if statements, they can solve this problem.

Interestingly, this problem very quickly turned into a meme because—as is the case with most programming debates—someone was upset with a particular way the problem was solved. In an effort to dunk on this person, the programming community united to find the worst ways of solving the problem.

My plan today is to share some of those solutions and possibly share some of my own! Let’s get into it.

The Initial Offender

To kick things off, here’s what the initial post looked like:

This code structure is called an arrow anti-pattern. How to fix this code?

public class ArrowAntiPattern
{
  public string CheckGrade(int score)
  {
    if (score >= 90)
    {
      return "A";
    }
    else
    {
      if (score >= 80)
      {
        return "B";
      }
      else
      {
        if (score >= 70)
        {
          return "C";
        }
        else
        {
          if (score >= 60)
          {
            return "D";
          }
          else
          {
            return "F";
          }
        }
      }
    }
  }
}
@ozanyrcOpens in a new tab.

Honestly, I agree this code could be cleaner, but I think most folks in the field know that. I think what sparked efforts to make the code much, much worse was actually this same author’s proposed solutions. Let’s take a look at one next!

When the Proposed Fix Is Somehow Worse

When crafting a solution to the “arrow anti-pattern,” the author proposed the following solution—which might as well be trolling:

This is a solution. Switch statement is ok, too.

using System;
using System.Collections.Generic;

public class ArrowAntiPattern
{
  private Dictionary<int, string> gradeMap = new Dictionary<int, String>()
  {
    { 90, "A" },
    { 80, "B" },
    { 70, "C" },
    { 60, "D" }
  };

  public string CheckGrade(int score)
  {
    foreach (var entry in gradeMap)
    {
      if (score >= entry.Key)
      {
        return entry.Value;
      }
    }
    return "F";
  }

  static void Main(string[] args)
  {
    ArrowAntiPattern app = new ArrowAntiPattern();
    Console.WriteLine(app.CheckGrade(85)); // Output: B
  }
}
@ozanyrcOpens in a new tab.

At a glance, you might chalk this up to overengineering, but overengineering implies that there was at least some engineering. Instead, there are a lot of issues.

To start, I’m no C# expert, but typically dictionaries are not ordered by definition. Therefore, I would expect this code to fail just on the premise that we cannot guarantee the order of the dictionary.

Second, the CheckGrade method is weirdly an instance method when it’s really a static method. No more is this apparent than its use in the main method, which requires the construction of an ArrowAntiPattern object to execute. Just make it static!

Third, it sort of bugs me that the dictionary doesn’t include a definition for “F,” so you end up with four of the letter grades defined in the dictionary and the fifth in some function elsewhere.

Lastly, and this probably the least reasonable of my complaints but whatever, you’ve removed nested if statements and replaced them with essentially the same exact logic, except as a loop. To me, this isn’t different enough of a solution to warrant a rewrite. What’s worse is that you’ve somewhat revived the arrow pattern because you have to nest an if statement in the loop anyway. You’d be better off just hardcoding a lookup table.

All in all, you can probably imagine that folks who saw this “solution” thought, “oh, we’re shitposting? Let me get in on that!” So, let’s move on and look at some of my favorite solutions.

Why Not Write a String Indexing Solution?

I think the initial solution I saw was this goofy string indexing solution, where a string is carefully constructed to contain the letter grades along buckets of tens:

ok guys how about this

static char CheckGrade( int score ) {
  return "FFFFFFDCBA"[score / 10];
}
@FreyaHolmerOpens in a new tab.

Even though this is a shitpost, I quite like the solution. Of course, it has a lot problems—like you can’t really handle different distributions, and there is a bug for folks who get a perfect score or greater. But since we agreed we’re all shitposting, I think this solution is pretty clever and a lot of fun.

Let’s Hardcode Every Case

You might be familiar with that one isEven meme where the solution is to write out every possible even number as a separate branch. I don’t know who originally posted it, but the one I always see is of YandereDev:

Anyhow, someone replicated it for their grade checking code:

i wish there was a better way to do this

public string CheckGrade(int score) {
  if (score == 100) {
    return "A";
  } else {
    if (score == 99) {
      return "A";
    } else {
      if (score == 98) {
        return "A";
      }  // ... you get the point
@2DArrayOpens in a new tab.

Naturally, this solution is ridiculous, but it works as-is. That’s really all the praise I can give it.

How About Some Truly Unreadable Code?

Eventually, the people who like to get real close to the metal came around and shared a nasty bit shifting solution.

I can only assume people proposing dictionaries are trolling, so here goes my contribution to the circus:

// s in [0..100] by contract
char CheckGrade( int s )
{
  assert( s>=0 && s<=100 );
  return "FFFFFFDCBA"
         [((s<<4)+(s<<3)+s+54)>>8];
}
@iquilezlesOpens in a new tab.

I’ll be honest; I don’t really know what this does. It looks very similar to the string indexing solution from before, but this one seems to be doing the division through addition and bit shifting. At any rate, I love it, and I want to see more like it.

Exceptions, Anyone?

While looking through some of the fun solutions folks generated, I saw this hilarious solution that used exception handling to produce letter grades:

I like the way you think!

try {
  try {
    if (score == 100) {
      throw new GradeAException();
    } else {
      if (score == 99) {
        throw new GradeAException();
      } else {
        if (score == 98) {
          // ...
        }
      }
    }
  } catch (GradeAException ex) {
    return "A";
  }
} catch (GradeBException ex) {
  return "B";
}
// etc.
@RunasSudoOpens in a new tab.

To me, this is like the funniest possible way to solve this problem. I take your nesting, and I raise you double nesting. The only way this could be more funny to me is if it somehow include the dictionary from the original “solution.”

Funny Alternatives & An Introduction to Obfuscation

As you can probably imagine, there are literally hundreds of ways of solving this problem. As a result, I only shared a few. That said, you can’t stop me from listing off some of the other ones I’ve seen:

  • Use the OpenAI API to ask GPT-4 for a letter grade directly
  • Use a massive fallthrough switch statement
  • Perform arithmetic on the ASCII values of the letter grades
  • Nest ternaries to get a single line solution
  • Find the closest letter grade using binary search
  • Use category theory?

With all that said, I think the main reason I enjoy this exercise so much is because I also enjoy the art of obfuscation. Surely, with each shitpost solution, we come closer and closer to the perfect way to obfuscate grade checking code. I want a solution that is so unreadable that you might as well just look at the compiler output.

Naturally, I’ve dabbled in obfuscation, so why not check out some of these related pieces:

That should give you enough to chew on until the next series of coding shitposts. Until then, thanks for sticking around. If you liked this article or any of the ones linked above, consider checking out my list of ways to grow the site. Otherwise, take care!

Jeremy Grifski

Jeremy grew up in a small town where he enjoyed playing soccer and video games, practicing taekwondo, and trading Pokémon cards. Once out of the nest, he pursued a Bachelors in Computer Engineering with a minor in Game Design. After college, he spent about two years writing software for a major engineering company. Then, he earned a master's in Computer Science and Engineering. Today, he pursues a PhD in Engineering Education in order to ultimately land a teaching gig. In his spare time, Jeremy enjoys spending time with his wife, playing Overwatch and Phantasy Star Online 2, practicing trombone, watching Penguins hockey, and traveling the world.

Recent Posts