Gibt es eine einfache Möglichkeit, Ordnungszahlen in C # zu erstellen?


202

Gibt es in C # eine einfache Möglichkeit, Ordnungszahlen für eine Zahl zu erstellen ? Beispielsweise:

  • 1 gibt den 1. zurück
  • 2 gibt den 2. zurück
  • 3 gibt den 3. Platz zurück
  • ...etc

Kann dies durchgeführt werden String.Format()oder stehen dafür Funktionen zur Verfügung?

Antworten:


310

Auf dieser Seite finden Sie eine vollständige Liste aller benutzerdefinierten numerischen Formatierungsregeln:

http://msdn.microsoft.com/en-us/library/0c899ak8.aspx

Wie Sie sehen können, enthält Ordinals nichts, daher kann dies nicht mit String.Format durchgeführt werden. Es ist jedoch nicht wirklich so schwer, eine Funktion dafür zu schreiben.

public static string AddOrdinal(int num)
{
    if( num <= 0 ) return num.ToString();

    switch(num % 100)
    {
        case 11:
        case 12:
        case 13:
            return num + "th";
    }

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

Update: Technisch gesehen gibt es keine Ordnungszahlen für <= 0, daher habe ich den obigen Code aktualisiert. Auch die redundanten ToString()Methoden entfernt.

Beachten Sie auch, dass dies nicht internationalisiert ist. Ich habe keine Ahnung, wie Ordnungszahlen in anderen Sprachen aussehen.


2
Assert.AreEqual ("0", AddOrdinal (0)); Siehe wisegeek.com/what-is-an-ordinal-number.htm
si618

2
Die Verwendung einer Erweiterungsmethode (oder wie auch immer sie heißt - siehe @ Stu's Antwort) würde hier hervorragend funktionieren. @ Si, das Hinzufügen dieser Bedingung wäre sehr einfach, wenn es erforderlich ist.
Strager

12
Ich habe '11., 12., 13.' vergessen ... sollte eine Interviewfrage sein. :-)
Holf

2
Ja, gut Programmierer sind komisch;)
Samjudson

2
@IanWarburton Es gibt keine Redundanz, da nur eine einzige return-Anweisung getroffen wird. Wenn Sie mit der Antwort nicht zufrieden sind, geben Sie bitte Ihre eigene an und zeigen Sie uns, wie dies "richtig" ist und warum es wichtig ist.
B2K

73

Denken Sie an die Internationalisierung!

Die Lösungen hier funktionieren nur für Englisch. Die Dinge werden viel komplexer, wenn Sie andere Sprachen unterstützen müssen.

Zum Beispiel würde "1st" auf Spanisch als "1.o", "1.a", "1.os" oder "1.as" geschrieben, je nachdem, ob das, was Sie zählen, männlich, weiblich oder plural ist !

Wenn Ihre Software verschiedene Sprachen unterstützen muss, vermeiden Sie Ordnungszahlen.


7
@ Andomar: "Die ersten 2 Leser" => auf Italienisch (und vermutlich auch auf Spanisch) "first" ist hier Plural. Sie haben also Singular männlich, Singular weiblich, Plural männlich, Plural weiblich; Vielleicht hat eine Sprache auch einen neutralen Fall (der Dinge von Menschen / Tieren unterscheidet)
M.Turrini

2
Das heißt, Sie müssen Ordnungszahlen nicht vermeiden: Nehmen Sie sie in die Lokalisierung auf, sobald Sie alle möglichen Fälle kennen, oder akzeptieren Sie (Ihren Kunden) einige Einschränkungen.
M. Turrini

26
Dies erklärt, warum das .NET-Team es vermieden hat, es den DateTime-Formatierern hinzuzufügen
Chris S

moment.js hat eine "ordinale" Formatierungsfunktion nach Gebietsschema, so dass dies machbar erscheint. Ich wünschte auch, sie hätten dies in .NET für DateTime
Guillaume86 am

5
Es wäre alles sehr einfach, wenn Sie alle das "." Zeichen für Ordnungszahlen, wie wir es auf Deutsch tun)))) 1. 2. 3. 4. 5. usw. Obwohl der Algorithmus umso interessanter wäre, wenn man die Zahl ausschreiben und die Flexion in 4 anhängen müsste grammatikalische Fälle mit 3 verschiedenen Artikeln, zusätzlich zu den Singular- und Pluralfällen der 12 verschiedenen Kombinationen. Kommen Sie und denken Sie darüber nach, haben die Russen nicht noch zwei plus Vokativ und einige nordische Sprachen haben 15, denke ich. Ich hätte diese Implementierung gerne in .NET gesehen.
Stefan Steiger

22

Meine Version von Jesses Version von Stu's und Samjudson's Versionen :)

Enthaltener Komponententest, um zu zeigen, dass die akzeptierte Antwort falsch ist, wenn Nummer <1

    /// <summary>
    /// Get the ordinal value of positive integers.
    /// </summary>
    /// <remarks>
    /// Only works for english-based cultures.
    /// Code from: http://stackoverflow.com/questions/20156/is-there-a-quick-way-to-create-ordinals-in-c/31066#31066
    /// With help: http://www.wisegeek.com/what-is-an-ordinal-number.htm
    /// </remarks>
    /// <param name="number">The number.</param>
    /// <returns>Ordinal value of positive integers, or <see cref="int.ToString"/> if less than 1.</returns>
    public static string Ordinal(this int number)
    {
        const string TH = "th";
        string s = number.ToString();

        // Negative and zero have no ordinal representation
        if (number < 1)
        {
            return s;
        }

        number %= 100;
        if ((number >= 11) && (number <= 13))
        {
            return s + TH;
        }

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

    [Test]
    public void Ordinal_ReturnsExpectedResults()
    {
        Assert.AreEqual("-1", (1-2).Ordinal());
        Assert.AreEqual("0", 0.Ordinal());
        Assert.AreEqual("1st", 1.Ordinal());
        Assert.AreEqual("2nd", 2.Ordinal());
        Assert.AreEqual("3rd", 3.Ordinal());
        Assert.AreEqual("4th", 4.Ordinal());
        Assert.AreEqual("5th", 5.Ordinal());
        Assert.AreEqual("6th", 6.Ordinal());
        Assert.AreEqual("7th", 7.Ordinal());
        Assert.AreEqual("8th", 8.Ordinal());
        Assert.AreEqual("9th", 9.Ordinal());
        Assert.AreEqual("10th", 10.Ordinal());
        Assert.AreEqual("11th", 11.Ordinal());
        Assert.AreEqual("12th", 12.Ordinal());
        Assert.AreEqual("13th", 13.Ordinal());
        Assert.AreEqual("14th", 14.Ordinal());
        Assert.AreEqual("20th", 20.Ordinal());
        Assert.AreEqual("21st", 21.Ordinal());
        Assert.AreEqual("22nd", 22.Ordinal());
        Assert.AreEqual("23rd", 23.Ordinal());
        Assert.AreEqual("24th", 24.Ordinal());
        Assert.AreEqual("100th", 100.Ordinal());
        Assert.AreEqual("101st", 101.Ordinal());
        Assert.AreEqual("102nd", 102.Ordinal());
        Assert.AreEqual("103rd", 103.Ordinal());
        Assert.AreEqual("104th", 104.Ordinal());
        Assert.AreEqual("110th", 110.Ordinal());
        Assert.AreEqual("111th", 111.Ordinal());
        Assert.AreEqual("112th", 112.Ordinal());
        Assert.AreEqual("113th", 113.Ordinal());
        Assert.AreEqual("114th", 114.Ordinal());
        Assert.AreEqual("120th", 120.Ordinal());
        Assert.AreEqual("121st", 121.Ordinal());
        Assert.AreEqual("122nd", 122.Ordinal());
        Assert.AreEqual("123rd", 123.Ordinal());
        Assert.AreEqual("124th", 124.Ordinal());
    }

15

Einfach, sauber, schnell

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

Oder noch besser als Erweiterungsmethode

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

Jetzt können Sie einfach anrufen

int a = 1;
a.DisplayWithSuffix(); 

oder sogar so direkt wie

1.DisplayWithSuffix();

14

Sie müssen Ihre eigenen rollen. Von oben:

public static string Ordinal(this int number)
{
  var work = number.ToString();
  if ((number % 100) == 11 || (number % 100) == 12 || (number % 100) == 13)
    return work + "th";
  switch (number % 10)
  {
    case 1: work += "st"; break;
    case 2: work += "nd"; break;
    case 3: work += "rd"; break;
    default: work += "th"; break;
  }
  return work;
}

Sie können dann tun

Console.WriteLine(432.Ordinal());

Bearbeitet für Ausnahmen vom 12.11.13. Ich habe von oben gesagt :-)

Bearbeitet für 1011 - andere haben dies bereits behoben. Sie möchten nur sicherstellen, dass andere diese falsche Version nicht verwenden.


12

Ich mochte eher Elemente aus Stu 's und Samjudsons Lösungen und arbeitete sie zu einer meiner Meinung nach verwendbaren Kombination zusammen:

    public static string Ordinal(this int number)
    {
        const string TH = "th";
        var s = number.ToString();

        number %= 100;

        if ((number >= 11) && (number <= 13))
        {
            return s + TH;
        }

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

1
Was ist der Grund für die Verwendung einer Konstante für "th"?
Nickf

weil es zweimal im Code verwendet wird. Verwenden Sie einfach die uralte Weisheit, dass Sie sich nicht wiederholen sollten :) In diesem Fall sollte die .NET-Laufzeit nur eine Kopie der Zeichenfolge erstellen, während mit zwei "th" im Code zwei Zeichenfolgen erstellt werden und im Speicher referenziert.
Jesse C. Slicer

25
und auch wenn sich der Wert von TH jemals ändert, werden Sie eingestellt.
Eclipse

7
@Jesse - Sie erhalten meine +1, aber ich glaube nicht, dass .NET Zeichenfolgen auf diese Weise behandelt, siehe yoda.arachsys.com/csharp/strings.html#interning . Meine Lektüre bezieht sich jeweils auf das "th" -Literal würde das gleiche Speicherbit referenzieren. Aber ich stimme DRY zu :)
si618

4
Das Entfernen solcher Duplikate behindert meiner Meinung nach nur die Lesbarkeit, daher die Verwirrung "Warum der TH?". Ich denke nicht, dass DRY so interpretiert werden sollte, dass alle Duplikate unabhängig von den Kosten entfernt werden.
SeeNoWeevil

8

Obwohl ich dies noch nicht bewertet habe, sollten Sie in der Lage sein, eine bessere Leistung zu erzielen, indem Sie alle bedingten Fallaussagen vermeiden.

Dies ist Java, aber ein Port nach C # ist trivial:

public class NumberUtil {
  final static String[] ORDINAL_SUFFIXES = {
    "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
  };

  public static String ordinalSuffix(int value) {
    int n = Math.abs(value);
    int lastTwoDigits = n % 100;
    int lastDigit = n % 10;
    int index = (lastTwoDigits >= 11 && lastTwoDigits <= 13) ? 0 : lastDigit;
    return ORDINAL_SUFFIXES[index];
  }

  public static String toOrdinal(int n) {
    return new StringBuffer().append(n).append(ordinalSuffix(n)).toString();
  }
}

Beachten Sie, dass die Reduzierung von Bedingungen und die Verwendung der Array-Suche die Leistung beschleunigen sollten, wenn viele Ordnungszahlen in einer engen Schleife generiert werden. Ich gebe jedoch auch zu, dass dies nicht so lesbar ist wie die Lösung für Fallanweisungen.


Es tut mir leid, dass ich dies in C # verglichen habe. Ihre Version ist nicht schneller als die Lösung von si618.
GY_

Überprüfen Sie diese Antwort stackoverflow.com/a/58378465/2583579 für einige Benchmarks
Dan Dohotaru

3

Ähnlich wie bei Ryans Lösung, aber noch grundlegender, verwende ich einfach ein einfaches Array und benutze den Tag, um die richtige Ordnungszahl nachzuschlagen:

private string[] ordinals = new string[] {"","st","nd","rd","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","st","nd","rd","th","th","th","th","th","th","th","st" };
DateTime D = DateTime.Now;
String date = "Today's day is: "+ D.Day.ToString() + ordinals[D.Day];

Ich hatte keine Notwendigkeit, aber ich würde annehmen, dass Sie ein mehrdimensionales Array verwenden könnten, wenn Sie Unterstützung für mehrere Sprachen wünschen.

Soweit ich mich aus meiner Uni-Zeit erinnern kann, erfordert diese Methode nur minimalen Aufwand vom Server.


2

Ich benutze diese Erweiterungsklasse:

public static class Int32Extensions
{
    public static string ToOrdinal(this int i)
    {
        return (i + "th")
            .Replace("1th", "1st")
            .Replace("2th", "2nd")
            .Replace("3th", "3rd");
    }
}

11., 12., 13.
Kcoder

2

Angeforderte "weniger Redundanz" -Version von Samjudsons Antwort ...

public static string AddOrdinal(int number)
{
    if (number <= 0) return number.ToString();

    string GetIndicator(int num)
    {
        switch (num % 100)
        {
            case 11:
            case 12:
            case 13:
                return "th";
        }

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

    return number + GetIndicator(number);
}

2
Ich würde "GetIndicator" als verfügbar machen public staticund es in einen mnemonischeren Namen umbenennen (dh "OrdinalSuffix"). Der Anrufer möchte möglicherweise den Nummernteil in verschiedenen Formaten (dh mit Kommas).
Tom

2
        private static string GetOrd(int num) => $"{num}{(!(Range(11, 3).Any(n => n == num % 100) ^ Range(1, 3).All(n => n != num % 10)) ? new[] { "ˢᵗ", "ⁿᵈ", "ʳᵈ" }[num % 10 - 1] : "ᵗʰ")}";

Wenn jemand nach einem Liner sucht: p


1
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";
}

1

EDIT : Wie YM_Industries im Kommentar hervorhebt , funktioniert Samjudsons Antwort für Zahlen über 1000, Nickfs Kommentar scheint verschwunden zu sein und ich kann mich nicht erinnern, was das Problem war, das ich gesehen habe. Lassen Sie diese Antwort hier für die Vergleichszeiten.

Sehr viele davon funktionieren nicht für Zahlen> 999, wie Nickf in einem Kommentar hervorhob (BEARBEITEN: jetzt fehlt).

Hier ist eine Version basierend aus einer modifizierten Version von samjudson ‚s akzeptierte Antwort , das tut.

public static String GetOrdinal(int i)
{
    String res = "";

    if (i > 0)
    {
        int j = (i - ((i / 100) * 100));

        if ((j == 11) || (j == 12) || (j == 13))
            res = "th";
        else
        {
            int k = i % 10;

            if (k == 1)
                res = "st";
            else if (k == 2)
                res = "nd";
            else if (k == 3)
                res = "rd";
            else
                res = "th";
        }
    }

    return i.ToString() + res;
}

Auch Shahzad Qureshi ‚s Antwort String - Manipulation unter Verwendung funktioniert gut, aber es ist eine Leistungseinbuße hat. Um viele davon zu generieren, macht ein LINQPad-Beispielprogramm die String-Version 6-7 mal langsamer als diese Ganzzahl (obwohl Sie viel generieren müssten, um es zu bemerken).

LINQPad-Beispiel:

void Main()
{
    "Examples:".Dump();

    foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 10000013 })
        Stuff.GetOrdinal(i).Dump();

    String s;

    System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();

    for(int iter = 0; iter < 100000; iter++)
        foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 1000013 })
            s = Stuff.GetOrdinal(i);

    "Integer manipulation".Dump();
    sw.Elapsed.Dump();

    sw.Restart();

    for(int iter = 0; iter < 100000; iter++)
        foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 1000013 })
            s = (i.ToString() + Stuff.GetOrdinalSuffix(i));

    "String manipulation".Dump();
    sw.Elapsed.Dump();
}

public class Stuff
{
        // Use integer manipulation
        public static String GetOrdinal(int i)
        {
                String res = "";

                if (i > 0)
                {
                        int j = (i - ((i / 100) * 100));

                        if ((j == 11) || (j == 12) || (j == 13))
                                res = "th";
                        else
                        {
                                int k = i % 10;

                                if (k == 1)
                                        res = "st";
                                else if (k == 2)
                                        res = "nd";
                                else if (k == 3)
                                        res = "rd";
                                else
                                        res = "th";
                        }
                }

                return i.ToString() + res;
        }

        // Use string manipulation
        public static string GetOrdinalSuffix(int num)
        {
                if (num.ToString().EndsWith("11")) return "th";
                if (num.ToString().EndsWith("12")) return "th";
                if (num.ToString().EndsWith("13")) return "th";
                if (num.ToString().EndsWith("1")) return "st";
                if (num.ToString().EndsWith("2")) return "nd";
                if (num.ToString().EndsWith("3")) return "rd";
                return "th";
        }
}

Ich kann den Kommentar von @ nickf nicht finden. Was ist falsch an Samjudsons Antwort? Es scheint mir, als würde es Zahlen über 1000 gut verarbeiten, während es viel besser lesbar ist als deins.
Joshua Walsh

1
Es ist ein fairer Kommentar, ich habe gerade einen Testsatz durchgeführt und kann keine Probleme finden. Es scheint auch keine Änderungen an Sams Antwort gegeben zu haben, also kann ich mir nur vorstellen, dass ich verrückt geworden bin. Ich habe meine Antwort bearbeitet, um dies widerzuspiegeln.
Whelkaholism

1
Haha, wir haben alle solche Momente, nicht wahr? Wir schauen zurück auf alten Code und sagen: "Warum zum Teufel habe ich das geschrieben?"
Joshua Walsh

1

Basierend auf den anderen Antworten:

public static string Ordinal(int n)
{   
    int     r = n % 100,     m = n % 10;

    return (r<4 || r>20) && (m>0 && m<4) ? n+"  stndrd".Substring(m*2,2) : n+"th";                                              
}

3
1. ORT: Unnötig kryptische Antwort. "Unnötig": Vorteile hinsichtlich Codegröße / Leistung, die keine Lesbarkeitskosten wert sind. "Kryptisch": Signifikante Übersetzung erforderlich, um den Anforderungen von "Laien" zuzuordnen.
Tom

0

FWIW, für MS-SQL erledigt dieser Ausdruck die Aufgabe. Behalten Sie das erste WHEN ( WHEN num % 100 IN (11, 12, 13) THEN 'th') als erstes in der Liste bei, da dies davon abhängt, dass Sie es vor den anderen versuchen.

CASE
  WHEN num % 100 IN (11, 12, 13) THEN 'th' -- must be tried first
  WHEN num % 10 = 1 THEN 'st'
  WHEN num % 10 = 2 THEN 'nd'
  WHEN num % 10 = 3 THEN 'rd'
  ELSE 'th'
END AS Ordinal

Für Excel:

=MID("thstndrdth",MIN(9,2*RIGHT(A1)*(MOD(A1-11,100)>2)+1),2)

Der Ausdruck (MOD(A1-11,100)>2)ist TRUE (1) für alle Zahlen mit Ausnahme der Endungen 11,12,13(FALSE = 0). So 2 * RIGHT(A1) * (MOD(A1-11,100)>2) +1)endet 1 für 11/12/13, andernfalls:
1 ergibt 3
2 bis 5,
3 bis 7
andere: 9
- und die erforderlichen 2 Zeichen werden "thstndrdth"ausgehend von dieser Position ausgewählt.

Wenn Sie das wirklich ziemlich direkt in SQL konvertieren möchten, hat dies für mich für eine Handvoll Testwerte funktioniert:

DECLARE @n as int
SET @n=13
SELECT SubString(  'thstndrdth'
                 , (SELECT MIN(value) FROM
                     (SELECT 9 as value UNION
                      SELECT 1+ (2* (ABS(@n) % 10)  *  CASE WHEN ((ABS(@n)+89) % 100)>2 THEN 1 ELSE 0 END)
                     ) AS Mins
                   )
                 , 2
                )

0

Dies ist die Implementierung in dartund kann je nach Sprache geändert werden.

String getOrdinalSuffix(int num){
    if (num.toString().endsWith("11")) return "th";
    if (num.toString().endsWith("12")) return "th";
    if (num.toString().endsWith("13")) return "th";
    if (num.toString().endsWith("1")) return "st";
    if (num.toString().endsWith("2")) return "nd";
    if (num.toString().endsWith("3")) return "rd";
    return "th";
}

0

Zwar gibt es hier viele gute Antworten, aber ich denke, es gibt Platz für eine andere, diesmal basierend auf dem Mustervergleich, wenn nicht für irgendetwas anderes, dann zumindest für eine umstrittene Lesbarkeit

    public static string Ordinals1(this int number)
    {
        switch (number)
        {
            case int p when p % 100 == 11:
            case int q when q % 100 == 12:
            case int r when r % 100 == 13:
                return $"{number}th";
            case int p when p % 10 == 1:
                return $"{number}st";
            case int p when p % 10 == 2:
                return $"{number}nd";
            case int p when p % 10 == 3:
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }

und was macht diese lösung so besonders? nichts als die Tatsache, dass ich einige Leistungsüberlegungen für verschiedene andere Lösungen hinzufüge

Ehrlich gesagt bezweifle ich, dass Leistung für dieses spezielle Szenario wirklich wichtig ist (wer wirklich die Ordnungszahlen von Millionen von Zahlen benötigt), aber zumindest zeigt es einige Vergleiche, die berücksichtigt werden müssen ...

1 Million Artikel als Referenz (Ihre Millage kann natürlich je nach Maschinenspezifikation variieren)

mit Mustervergleich und Unterteilungen (diese Antwort)

~ 622 ms

mit Pattern Matching und Strings (diese Antwort)

~ 1967 ms

mit zwei Schaltern und Abteilungen (akzeptierte Antwort)

~ 637 ms

mit einem Schalter und Abteilungen (eine andere Antwort)

~ 725 ms

void Main()
{
    var timer = new Stopwatch();
    var numbers = Enumerable.Range(1, 1000000).ToList();

    // 1
    timer.Reset();
    timer.Start();
    var results1 = numbers.Select(p => p.Ordinals1()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with pattern matching and divisions");

    // 2
    timer.Reset();
    timer.Start();
    var results2 = numbers.Select(p => p.Ordinals2()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with pattern matching and strings");

    // 3
    timer.Reset();
    timer.Start();
    var results3 = numbers.Select(p => p.Ordinals3()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with two switches and divisons");

    // 4
    timer.Reset();
    timer.Start();
    var results4 = numbers.Select(p => p.Ordinals4()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with one switche and divisons");
}

public static class Extensions
{
    public static string Ordinals1(this int number)
    {
        switch (number)
        {
            case int p when p % 100 == 11:
            case int q when q % 100 == 12:
            case int r when r % 100 == 13:
                return $"{number}th";
            case int p when p % 10 == 1:
                return $"{number}st";
            case int p when p % 10 == 2:
                return $"{number}nd";
            case int p when p % 10 == 3:
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }

    public static string Ordinals2(this int number)
    {
        var text = number.ToString();
        switch (text)
        {
            case string p when p.EndsWith("11"):
                return $"{number}th";
            case string p when p.EndsWith("12"):
                return $"{number}th";
            case string p when p.EndsWith("13"):
                return $"{number}th";
            case string p when p.EndsWith("1"):
                return $"{number}st";
            case string p when p.EndsWith("2"):
                return $"{number}nd";
            case string p when p.EndsWith("3"):
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }

    public static string Ordinals3(this int number)
    {
        switch (number % 100)
        {
            case 11:
            case 12:
            case 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 Ordinals4(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}th";
            case 2:
                return $"{number}nd";
            case 3:
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }
}

0

Ein weiterer Einzeiler, jedoch ohne Vergleiche, indem nur das Regex-Ergebnis in ein Array indiziert wird.

public static string GetOrdinalSuffix(int input)
{
    return new []{"th", "st", "nd", "rd"}[Convert.ToInt32("0" + Regex.Match(input.ToString(), "(?<!1)[1-3]$").Value)];
}

Die PowerShell-Version kann weiter gekürzt werden:

function ord($num) { return ('th','st','nd','rd')[[int]($num -match '(?<!1)[1-3]$') * $matches[0]] }

0

Ein weiterer 1 Liner.

public static string Ordinal(this int n)
{    
 return n + (new [] {"st","nd","rd" }.ElementAtOrDefault((((n + 90) % 100 - 10) % 10 - 1)) ?? "th");
}

-2

Hier ist die DateTime-Erweiterungsklasse. Kopieren, Einfügen und Genießen

öffentliche statische Klasse DateTimeExtensions {

    public static string ToStringWithOrdinal(this DateTime d)
    {
        var result = "";
        bool bReturn = false;            

        switch (d.Day % 100)
        {
            case 11:
            case 12:
            case 13:
                result = d.ToString("dd'th' MMMM yyyy");
                bReturn = true;
                break;
        }

        if (!bReturn)
        {
            switch (d.Day % 10)
            {
                case 1:
                    result = d.ToString("dd'st' MMMM yyyy");
                    break;
                case 2:
                    result = d.ToString("dd'nd' MMMM yyyy");
                    break;
                case 3:
                    result = d.ToString("dd'rd' MMMM yyyy");
                    break;
                default:
                    result = d.ToString("dd'th' MMMM yyyy");
                    break;
            }

        }

        if (result.StartsWith("0")) result = result.Substring(1);
        return result;
    }
}

Ergebnis:

9. Oktober 2014


Sie duplizieren: a) die Datumsformatzeichenfolge (X5) und b) den gesamten Rest der Methode (wenn der wahrscheinliche Anwendungsfall auftritt (falls noch nicht geschehen), dass ein Ordnungssuffix für Nicht-Tag des Monats erforderlich ist Zwecke oder sogar einen Tag des Monats mit einer anderen Datumsformatzeichenfolge). Verwenden Sie die von mir vorgeschlagene "OrdinalSuffix" -Methode aus Ian Warburtons Antwort vom 6. April 17 um 16:32 Uhr ( stackoverflow.com/questions/20156/… ).
Tom

-3

Eine andere Alternative, die ich basierend auf allen anderen Vorschlägen verwendet habe, für die jedoch kein spezielles Gehäuse erforderlich ist:

    public static string DateSuffix(int day)
    {
        if (day == 11 | day == 12 | day == 13) return "th";
        Math.DivRem(day, 10, out day);
        switch (day)
        {
            case 1:
                return "st";
            case 2:
                return "nd";
            case 3:
                return "rd";
            default:
                return "th";
        }
    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.