Converting Cardinal Numbers to Ordinal using C#

Introduction

A problem that often arises in practice is how to convert a cardinal number to its ordinal equivalent.

As the .NET Framework doesn't contain any methods that perform this particular conversion, this article presents some extension methods that can be used for the purpose.

What are cardinal and ordinal numbers anyway?

A cardinal number is simply one of the 'natural numbers', including zero, namely 0, 1, 2, 3, 4, 5,...

An ordinal number gives the position of something in a list 0th, 1st, 2nd, 3rd, 4th, 5th,...

The suffixes are derived from how ordinal numbers are written first, second, third, fourth, fifth, ...

Notice that negative numbers are not included in the sets of cardinal and ordinal numbers.

The rule for determining the ordinal equivalent of a cardinal number is that if the number ends with

  • 1 then add "st"
  • 2 then add "nd"
  • 3, then add "rd"
  • Otherwise, add "th"

The only exception to this rule is that if the number divided by 100 equals 11, 12, or 13, then "th" should be added rather than the above suffixes.

Are there any problems in programming the conversion methods?

Not really, If the input is a type of integer, then the coding is very easy. We just need to code extension methods for the 'long' and 'ulong' types. Methods for the other integral types can then simply call one or other of these two methods.

If a negative number is input, then the method can simply return the number (converted to a string) rather than throw an exception.

What if the number is expressed in words?

This is still quite easy.

All we need is a dictionary to map numeric words to their ordinal equivalents.

This dictionary can then be used to do a rough check that the input is a valid numeric string and, if it is, the final (or only) word can be extracted, converted to its ordinal equivalent, and re-attached to the rest of the string. If the rough check fails to confirm that it's a valid numeric string (for example, it includes the word 'minus'), then the input can simply be returned unaltered.

A slight problem is to replace the final word using the original character casing. This is easy enough if the final word is wholly upper or lowercase. Where it's mixed case, I decided to replace it using the 'title' case (i.e. only the first letter is capitalized).

Source code

The source code for three of the extension methods is reproduced below, namely for long, int, and string inputs, together with some test code

using System;
using System.Collections.Generic;

namespace Utilities
{
    static class MathExtensions
    {
        public static string ToOrdinal(this long number)
        {
            if (number < 0)
                return number.ToString();

            long rem = number % 100;
            if (rem >= 11 && rem <= 13)
                return number + "th";

            switch (number % 10)
            {
                case 1:
                    return number + "st";
                case 2:
                    return number + "nd";
                case 3:
                    return number + "rd";
                default:
                    return number + "th";
            }
        }

        public static string ToOrdinal(this int number)
        {
            return ((long)number).ToOrdinal();
        }

        public static string ToOrdinal(this string number)
        {
            if (string.IsNullOrEmpty(number))
                return number;

            var dict = new Dictionary<string, string>
            {
                { "zero", "zeroth" },
                { "nought", "noughth" },
                { "one", "first" },
                { "two", "second" },
                // ... add more mappings ...
                { "quintillion", "quintillionth" }
            };

            // ... remaining code ...

            return number.Substring(0, index + 1) + last;
        }
    }
}

class Program
{
    static void Main()
    {
        int[] ia = { 0, 1, 2, 3, 4, 11, 12, 13, 15, 23, 99, 1001, 72003, -2 };
        foreach (int i in ia)
        {
            Console.WriteLine("{0} -> {1}", i, i.ToOrdinal());
        }

        Console.WriteLine();

        string[] sa = { "Zero", "one", "TWO", "THree", "Twenty-One", "One hundred and eight", "Thousand-One", "Minus Seven", "Nine words" };
        foreach (string s in sa)
        {
            Console.WriteLine("{0} -> {1}", s, s.ToOrdinal());
        }

        Console.ReadKey();
    }
}

Results

The following is a screen-shot showing the results of running this program

CrdnlNum.jpg

Notice that -2, 'Minus Seven', and 'Nine words' are returned unaltered as they are invalid inputs.

Conclusion

Although these methods are not difficult to code, anything that means we have to 're-invent less wheels' is generally welcome.

The code for the integral types is other than long, and it is included in the accompanying download.

Notice that the extension method, which takes a string input, can be used as an adjunct to my NumberToText method (http://www.c-sharpcorner.com/uploadfile/b942f9/converting-numbers-to-words-in-C-Sharp/ ) where an ordinal rather than a cardinal representation is required.


Similar Articles