Is there an easy way in .NET to get "st", "nd", "rd" and "th" endings for numbers? [duplicate]
Asked Answered
A

11

73

I am wondering if there is a method or format string I'm missing in .NET to convert the following:

   1 to 1st
   2 to 2nd
   3 to 3rd
   4 to 4th
  11 to 11th
 101 to 101st
 111 to 111th

This link has a bad example of the basic principle involved in writing your own function, but I am more curious if there is an inbuilt capacity I'm missing.

Solution

Scott Hanselman's answer is the accepted one because it answers the question directly.

For a solution however, see this great answer.

Acalia answered 16/9, 2008 at 3:53 Comment(3)
They're called ordinal numbers (1st, 2nd, etc.) as opposed to cardinal numbers (1,2,3, etc.), FYI.Polyneuritis
This was answered quite elegantly here: https://mcmap.net/q/117320/-is-there-an-easy-way-to-create-ordinals-in-c#Rollway
Too bad this question seems to already have an answer, but I would suggest try using Humanizer library which you can install through nugget: github.com/Humanizr/Humanizer#ordinalize 1.Ordinalize() => "1st" 5.Ordinalize() => "5th"Jewett
H
60

No, there is no inbuilt capability in the .NET Base Class Library.

Herder answered 16/9, 2008 at 3:56 Comment(2)
This is a late comment, but is there any plans to add this capability to the .NET BCL to format dates?Frontogenesis
How about now, 2019?Paripinnate
C
87

It's a function which is a lot simpler than you think. Though there might be a .NET function already in existence for this, the following function (written in PHP) does the job. It shouldn't be too hard to port it over.

function ordinal($num) {
    $ones = $num % 10;
    $tens = floor($num / 10) % 10;
    if ($tens == 1) {
        $suff = "th";
    } else {
        switch ($ones) {
            case 1 : $suff = "st"; break;
            case 2 : $suff = "nd"; break;
            case 3 : $suff = "rd"; break;
            default : $suff = "th";
        }
    }
    return $num . $suff;
}
Chiropody answered 16/9, 2008 at 3:57 Comment(7)
What about localization?Electroencephalograph
Localization will mean that you have to create separate functions for each language. In german, you could just append "ter", but "1ter" "2ter" "3ter" looks really bad even though it's grammatically correct. In french, it's a bit better, but there is no universal way for every language.Inoperative
@Michael Stum: I'm not too familiar with all the international ordinal formats but would a string.Format(resString, number) suffice? Or do some languages not combine numbers with ordinal (pre/suff)ixes?Acalia
@MichaelStum: Actually in german you could NOT just add "ter". Consider "Heute ist der 1te Januar" (today is 1st of January). Or "Klicken Sie den 5ten Button" (click the 5th button). Just to name two of dozens of cases. You have to consider the proper Flexion (engl. inflection) for every single use.Farthest
Combining a number and such a suffix is unusual in German. You either write it as "1." or "erster"/"erste". The latter is generally used is texts and rarely needs to be generated automatically.Remuneration
Won't this fail for numbers between 10 and 20?Darees
@Darees Nope, there's a condition for $tensTighten
S
83

Simple, clean, quick

    private static string GetOrdinalSuffix(int num)
    {
        string number = num.ToString();
        if (number.EndsWith("11")) return "th";
        if (number.EndsWith("12")) return "th";
        if (number.EndsWith("13")) return "th";
        if (number.EndsWith("1")) return "st";
        if (number.EndsWith("2")) return "nd";
        if (number.EndsWith("3")) return "rd";
        return "th";
    }

Or better yet, as an extension method

public static class IntegerExtensions
{
    public static string DisplayWithSuffix(this int num)
    {
        string number = num.ToString();
        if (number.EndsWith("11")) return number + "th";
        if (number.EndsWith("12")) return number + "th";
        if (number.EndsWith("13")) return number + "th";
        if (number.EndsWith("1")) return number + "st";
        if (number.EndsWith("2")) return number + "nd";
        if (number.EndsWith("3")) return number + "rd";
        return number + "th";
    }
}

Now you can just call

int a = 1;
a.DisplayWithSuffix(); 

or even as direct as

1.DisplayWithSuffix();
Salomie answered 23/10, 2013 at 22:37 Comment(7)
Probably the neatest answer here.Acalia
I think this is the cleanest way to do itRene
Hands down best answer. This actually relies on number as text, rather than trying to use some complicated mathematical formula. This is exactly how the human brain itself would figure it out, and that's ideal.Sunderance
This is a good answer but if the method is called DisplayWithSuffix then the returned answer should include the number so 1 would return "1st" not just "st".Lacy
Good point, HitLikeAHammer. I will tweak the code to reflect that.Salomie
You probably want to put num.ToString() into a variable. Not sure how anyone would consider this answer either simple, clean or quick to be honest.Properly
@Properly Exactly, -1.Ninurta
H
60

No, there is no inbuilt capability in the .NET Base Class Library.

Herder answered 16/9, 2008 at 3:56 Comment(2)
This is a late comment, but is there any plans to add this capability to the .NET BCL to format dates?Frontogenesis
How about now, 2019?Paripinnate
A
55

@nickf: Here is the PHP function in C#:

public static string Ordinal(int number)
{
    string suffix = String.Empty;

    int ones = number % 10;
    int tens = (int)Math.Floor(number / 10M) % 10;

    if (tens == 1)
    {
        suffix = "th";
    }
    else
    {
        switch (ones)
        {
            case 1:
                suffix = "st";
                break;

            case 2:
                suffix = "nd";
                break;

            case 3:
                suffix = "rd";
                break;

            default:
                suffix = "th";
                break;
        }
    }
    return String.Format("{0}{1}", number, suffix);
}
Administrative answered 16/9, 2008 at 4:11 Comment(4)
Ha thanks, just about to post the code I wrote out. Yours beats mine anyway with the String.Format bit I think.Acalia
1) Why the conversion to decimal? A simple (number / 10) % 10 does the trick. 2) Why do you initialize suffix to a value that will never be used?Remuneration
@CodesInChaos: Without the conversion to decimal, you get a compiler error: The call is ambiguous between the following methods or properties: 'System.Math.Floor(decimal)' and 'System.Math.Floor(double)'. Initializing suffix to String.Empty is mostly habit but also helps to avoid accidental Use of unassigned local variable 'suffix' errors.Administrative
@ScottDorman 1) Only if you leave in the call to Floor which is nonsensical on integers. Integer division simply truncates towards zero, no need for casting to decimal or using Floor. (number / 10) % 10 is simpler and works. 2) Those errors occur if you overlooked a code path. A compiler error tells you to fix that mistake instead of silently returning a useless value.Remuneration
Z
14

This has already been covered but I'm unsure how to link to it. Here is the code snippit:

    public static string Ordinal(this int number)
    {
        var ones = number % 10;
        var tens = Math.Floor (number / 10f) % 10;
        if (tens == 1)
        {
            return number + "th";
        }

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

FYI: This is as an extension method. If your .NET version is less than 3.5 just remove the this keyword

[EDIT]: Thanks for pointing that it was incorrect, that's what you get for copy / pasting code :)

Zipporah answered 16/9, 2008 at 3:58 Comment(6)
Doesn't work. 1011 % 10 == 1. 1011st is incorrect.Acalia
I like how you declare the ones variable and never use it.Surfacetoair
@MattMitchell In your example, it would be 10110th not 1011stLussi
@EOLeary not sure what you're saying, but I think Marshall's edit catered for my example.Acalia
Why the use of float instead of plain integers? I'd use (number / 10) % 10.Remuneration
No need to use Math.Floor either; the division discards decimals so long as both numbers are int. Should read as var tens = number / 10 % 10;Query
K
9

Here's a Microsoft SQL Server Function version:

CREATE FUNCTION [Internal].[GetNumberAsOrdinalString]
(
    @num int
)
RETURNS nvarchar(max)
AS
BEGIN

    DECLARE @Suffix nvarchar(2);
    DECLARE @Ones int;  
    DECLARE @Tens int;

    SET @Ones = @num % 10;
    SET @Tens = FLOOR(@num / 10) % 10;

    IF @Tens = 1
    BEGIN
        SET @Suffix = 'th';
    END
    ELSE
    BEGIN

    SET @Suffix = 
        CASE @Ones
            WHEN 1 THEN 'st'
            WHEN 2 THEN 'nd'
            WHEN 3 THEN 'rd'
            ELSE 'th'
        END
    END

    RETURN CONVERT(nvarchar(max), @num) + @Suffix;
END
Ketron answered 4/3, 2009 at 15:51 Comment(3)
I just wrote that function almost verbatim! Differences: master db, cast instead of convert, and I use slightly different indenting. Great minds, I guess...Surfacetoair
+1 - Just had the need for a SQL version - saved me writing oneReckon
Superb, but only if we are fetching from SQL. But in this case I am formatting a .net DateTime variable. But this function will be immensely useful.Cosmotron
S
2

I know this isn't an answer to the OP's question, but because I found it useful to lift the SQL Server function from this thread, here is a Delphi (Pascal) equivalent:

function OrdinalNumberSuffix(const ANumber: integer): string;
begin
  Result := IntToStr(ANumber);
  if(((Abs(ANumber) div 10) mod 10) = 1) then // Tens = 1
    Result := Result + 'th'
  else
    case(Abs(ANumber) mod 10) of
      1: Result := Result + 'st';
      2: Result := Result + 'nd';
      3: Result := Result + 'rd';
      else
        Result := Result + 'th';
    end;
end;

Does ..., -1st, 0th make sense?

Sarcoma answered 15/2, 2012 at 8:53 Comment(0)
W
1

Another flavor:

/// <summary>
/// Extension methods for numbers
/// </summary>
public static class NumericExtensions
{
    /// <summary>
    /// Adds the ordinal indicator to an integer
    /// </summary>
    /// <param name="number">The number</param>
    /// <returns>The formatted number</returns>
    public static string ToOrdinalString(this int number)
    {
        // Numbers in the teens always end with "th"

        if((number % 100 > 10 && number % 100 < 20))
            return number + "th";
        else
        {
            // Check remainder

            switch(number % 10)
            {
                case 1:
                    return number + "st";

                case 2:
                    return number + "nd";

                case 3:
                    return number + "rd";

                default:
                    return number + "th";
            }
        }
    }
}
Winfredwinfrey answered 12/12, 2013 at 17:25 Comment(1)
Actually a good answer. But it is generic, i.e not specific to dates of a month. I meant only dates. So above 100 may be not applicable.Cosmotron
M
0
public static string OrdinalSuffix(int ordinal)
{
    //Because negatives won't work with modular division as expected:
    var abs = Math.Abs(ordinal); 

    var lastdigit = abs % 10; 

    return 
        //Catch 60% of cases (to infinity) in the first conditional:
        lastdigit > 3 || lastdigit == 0 || (abs % 100) - lastdigit == 10 ? "th" 
            : lastdigit == 1 ? "st" 
            : lastdigit == 2 ? "nd" 
            : "rd";
}
Mediator answered 16/12, 2013 at 11:52 Comment(0)
M
-3
else if (choice=='q')
{
    qtr++;

    switch (qtr)
    {
        case(2): strcpy(qtrs,"nd");break;
        case(3):
        {
           strcpy(qtrs,"rd");
           cout<<"End of First Half!!!";
           cout<<" hteam "<<"["<<hteam<<"] "<<hs;
           cout<<" vteam "<<" ["<<vteam;
           cout<<"] ";
           cout<<vs;dwn=1;yd=10;

           if (beginp=='H') team='V';
           else             team='H';
           break;
       }
       case(4): strcpy(qtrs,"th");break;
Malkamalkah answered 22/12, 2008 at 14:7 Comment(1)
come on. what are you writing? It has nothing to do with my question.Cosmotron
C
-6

I think the ordinal suffix is hard to get... you basically have to write a function that uses a switch to test the numbers and add the suffix.

There's no reason for a language to provide this internally, especially when it's locale specific.

You can do a bit better than that link when it comes to the amount of code to write, but you have to code a function for this...

Camarillo answered 16/9, 2008 at 3:59 Comment(1)
Given all the currency localisation strings etc. it seems a little stretch to add ordinal suffix.Acalia

© 2022 - 2024 — McMap. All rights reserved.