Changes in the C# language allow us to write more concise code than ever before, and it’s fun to see what solutions developers write. In this short but fun post, I’ll be writing a method that takes in an int and converts it into its ordinal position, the kind of output you might see on a leaderboard. So, for example, the value of 1 would be 1st, the value of 2 is 2nd, and so on.

The Ranking Problem

Given we have a collection of integers, we want to convert those values into their human-readable ordinal values. In English, we describe position using the suffixes of st, nd, rd, and th. So, for example, a collection of integers containing the values of 1, 2, 3, 4 would be equivalent to 1st, 2nd, 3rd, 4th. There are a few rules to keep in mind when dealing with numbers and their possible suffixes:

  1. When a value of 1 is in the position of the one whole number part, it will have a suffix of st unless the value in the tens whole-number part is 11; in that case, the suffix is th.
  2. When a value of 2 is in the position of the one whole number part, it will have a suffix of nd unless the value in the tens whole-number part is 12; in that case, the suffix is th.
  3. When a value of 3 is in the position of the one whole number part, it will have a suffix of rd unless the value in the tens whole-number part is 13; in that case, the suffix is th.
  4. In all other scenarios, the suffix will be th.

In general, the only numbers we should be cautious about are the ones that end in 1, 2, 3, 11, 12, and 13. We may treat all other numbers the same with the th suffix.

Let’s write some code.

Using Pattern Matching For Pretty Output

Given our rules, we can use C#’s pattern matching to define our rules around a provided int value. Our first step is to get the whole number part of our integer, and we can do that by using the remainder operator.

var ones = value % 10;
var tens = value % 100;

Now that we know the whole-number parts of our integer value, we can write our pattern matching switch statement.

var suffix = ones switch {
    1 when tens != 11 => "st",
    2 when tens != 12 => "nd",
    3 when tens != 13 => "rd",
    _ => "th"
};

Now, let’s put it all together. The method should be straightforward to implement from here.

string Ranking(int value)
{
    var ones = value % 10;
    var tens = value % 100;

    var suffix = ones switch {
        1 when tens != 11 => "st",
        2 when tens != 12 => "nd",
        3 when tens != 13 => "rd",
        _ => "th"
    };

    return string.Concat(value.ToString("N0"), suffix);
}

I end up using the string formatter of N0 to format the integer with zero (0) decimal placeholders. Let’s run the method in a console application.

using static System.Console;
using static System.String;

string Ranking(int value)
{
    var ones = value % 10;
    var tens = value % 100;

    var suffix = ones switch {
        1 when tens != 11 => "st",
        2 when tens != 12 => "nd",
        3 when tens != 13 => "rd",
        _ => "th"
    };

    return Concat(value.ToString("N0"), suffix);
}

var low = new List<int> { 1, 2, 3, 4, 11 }.Select(Ranking);
var high = new[] {1_001, 1_002, 1_003, 1_004, 1_011 }.Select(Ranking);

WriteLine($"First collection: {Join(", ", low) }");
WriteLine($"Second collection: {Join(", ", high)}");

Writing the app, we get the following application output.

First collection: 1st, 2nd, 3rd, 4th, 11th
Second collection: 1,001st, 1,002nd, 1,003rd, 1,004th, 1,011th

Neat! I hope you enjoyed this short post about C# pattern matching, and let me know if you think you could improve it by reaching me on Twitter @buhakmeh. Thanks for reading my posts and sharing them with friends and colleagues.