C'è un modo semplice per creare ordinali in C #?


202

Esiste un modo semplice in C # per creare gli ordinali per un numero? Per esempio:

  • 1 ritorna 1 °
  • 2 ritorna 2 °
  • 3 ritorni 3 °
  • ...eccetera

Questo può essere fatto attraverso String.Format()o ci sono funzioni disponibili per farlo?

Risposte:


311

Questa pagina offre un elenco completo di tutte le regole di formattazione numerica personalizzate:

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

Come puoi vedere, non c'è nulla di ordinale, quindi non può essere fatto usando String.Format. Tuttavia non è poi così difficile scrivere una funzione per farlo.

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

Aggiornamento: tecnicamente gli ordinali non esistono per <= 0, quindi ho aggiornato il codice sopra. Rimossi anche i ToString()metodi ridondanti .

Inoltre, questo non è internazionalizzato. Non ho idea dell'aspetto degli ordinali in altre lingue.


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

2
Usare un metodo di estensione (o come si chiama - vedi la risposta di @ Stu) funzionerebbe benissimo qui. @Si, Aggiungere questa condizione sarebbe molto semplice se richiesto.
Strager

12
Dimenticato "11, 12 13" ... dovrebbe essere una domanda di intervista. :-)
Holf

2
Sì, anche i programmatori sono strani;)
Samjudson,

2
@IanWarburton Non c'è ridondanza poiché verrà colpita una sola dichiarazione di ritorno. Se non sei soddisfatto della risposta, fornisci la tua, mostrandoci il modo "corretto" per farlo e perché è importante.
B2K,

73

Ricorda l'internazionalizzazione!

Le soluzioni qui funzionano solo per l'inglese. Le cose diventano molto più complesse se devi supportare altre lingue.

Ad esempio, in spagnolo "1st" verrebbe scritto come "1.o", "1.a", "1.os" o "1.as" a seconda che la cosa che stai contando sia maschile, femminile o plurale !

Quindi, se il tuo software deve supportare lingue diverse, cerca di evitare gli ordinali.


7
@ Andomar: "I primi 2 lettori" => in italiano (e anche in spagnolo, suppongo) "primo" è plurale qui. Quindi hai singolare maschile, singolare femminile, plurale maschile, plurale femminile; forse un po 'di lingua ha anche un caso neutro (distinguendo le cose da uomini / animali)
M.Turrini,

2
Detto questo, non devi evitare gli ordinali: includili nella localizzazione, una volta che conosci tutti i casi che potresti affrontare o (fai in modo che il tuo cliente) accetti alcune limitazioni.
M.Turrini,

26
Questo spiega perché il team .NET ha evitato di aggiungerlo ai formattatori DateTime
Chris S,

moment.js ha una funzione di formattazione "ordinale" in base alle impostazioni locali, quindi sembra fattibile, vorrei anche che lo avessero fatto in .NET per DateTime
Guillaume86

5
Sarebbe tutto molto semplice se tutti usassimo il "." carattere per ordinali, come facciamo in tedesco))))) 1. 2. 3. 4. 5., ecc. Sebbene l'algoritmo sarebbe molto più interessante se si scrivesse il numero e si dovesse aggiungere inflessione in 4 casi grammaticali con 3 articoli diversi, oltre ai casi singolari e plurali delle 12 diverse combinazioni. Vieni a pensarci bene, i russi non ne hanno altri 2, più vocativ, e alcune lingue nordiche ne hanno 15, credo. Mi sarebbe piaciuto vedere quell'implementazione in .NET.
Stefan Steiger,

22

La mia versione della versione di Jesse delle versioni di Stu e Samjudson :)

Test unitario incluso per dimostrare che la risposta accettata non è corretta quando il numero <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

Semplice, pulito, veloce

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

O meglio ancora, come metodo di estensione

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

Adesso puoi semplicemente chiamare

int a = 1;
a.DisplayWithSuffix(); 

o anche diretto come

1.DisplayWithSuffix();

14

Dovrai farlo da solo. Dalla cima della mia testa:

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

Quindi puoi farlo

Console.WriteLine(432.Ordinal());

Modificato per eccezioni l'11 / 12/13. Ho detto dall'alto della mia testa :-)

Modificato per 1011 - altri hanno già risolto questo problema, voglio solo assicurarsi che altri non prendano questa versione errata.


12

Mi sono piuttosto piaciuti gli elementi delle soluzioni di Stu e Samjudson e li ho uniti insieme in quella che penso sia una combinazione utilizzabile:

    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
qual è la logica dietro l'utilizzo di una costante per "th"?
Nickf,

perché è usato due volte nel codice. Utilizzando solo l'antica saggezza che non dovresti ripetere :) In questo caso, il runtime .NET dovrebbe creare solo una copia della stringa mentre con due "th" nel codice, ci sarebbero due stringhe create e referenziato in memoria.
Jesse C. Slicer,

25
e anche, se il valore di TH dovesse mai cambiare, verrai impostato.
Eclipse,

7
@Jesse - Ottieni il mio +1, ma non credo che .NET gestisca le stringhe in questo modo, vedi yoda.arachsys.com/csharp/strings.html#interning , la mia lettura di questo è ogni riferimento al "th" letterale farebbe riferimento allo stesso bit di memoria. Ma sono d'accordo su DRY :)
si618

4
Rimuovere la duplicazione in questo modo ostacola solo la leggibilità, quindi la confusione "Perché il TH?". Non credo che DRY debba essere interpretato come "rimuove tutte le duplicazioni qualunque sia il costo".
NoWeevil,

8

Anche se non l'ho ancora testato, dovresti essere in grado di ottenere prestazioni migliori evitando tutte le dichiarazioni del caso condizionale.

Questo è Java, ma una porta per C # è banale:

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();
  }
}

Si noti che la riduzione dei condizionali e l'uso della ricerca dell'array dovrebbero accelerare le prestazioni se si generano molti ordinali in un circuito stretto. Tuttavia, concedo anche che questo non è leggibile come la soluzione dell'istruzione case.


Mi dispiace di averlo confrontato in C #, la tua versione non è più veloce della soluzione di si618.
GY_

controlla questa risposta stackoverflow.com/a/58378465/2583579 per alcuni benchmark
Dan Dohotaru,

3

Simile alla soluzione di Ryan, ma ancora più semplice, uso solo un array semplice e uso il giorno per cercare l'ordinale corretto:

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];

Non ne ho avuto la necessità, ma suppongo che potresti usare un array multidimensionale se desideri avere il supporto di più lingue.

Da quello che posso ricordare dai miei giorni Uni, questo metodo richiede uno sforzo minimo dal server.


2

Uso questa classe di estensione:

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

Versione "meno ridondante" richiesta della risposta di samjudson ...

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
Vorrei esporre "GetIndicator" come a public statice rinominarlo con un nome più mnemonico (ad esempio "OrdinalSuffix"). Il chiamante potrebbe volere la parte numerica in diversi formati (es. Con virgole).
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] : "ᵗʰ")}";

Se qualcuno cerca una fodera: 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 : Come YM_Industries sottolinea nel commento, la risposta di Samjudson funziona per numeri superiori a 1000, il commento di Nickf sembra essere andato e non riesco a ricordare quale sia stato il problema. Lasciare questa risposta qui per i tempi di confronto.

Molti di questi non funzionano per numeri> 999, come ha sottolineato Nickf in un commento (EDIT: ora mancante).

Ecco una versione in base al largo una versione modificata del samjudson 's risposta accettata che fa.

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

Anche la risposta di Shahzad Qureshi usando la manipolazione di stringhe funziona bene, tuttavia ha una penalità di prestazione. Per generare molti di questi, un programma di esempio LINQPad rende la versione della stringa 6-7 volte più lenta di quella intera (anche se dovresti generare molto da notare).

Esempio LINQPad:

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

Non riesco a trovare il commento di @ Nickf, cosa c'è di sbagliato nella risposta di Samjudson? Mi sembra che gestisca numeri superiori a 1000 e che sia molto più leggibile dei tuoi.
Joshua Walsh,

1
È un commento corretto, ho appena eseguito un set di test e non riesco a trovare alcun problema. Non sembra esserci stata alcuna modifica alla risposta di Sam, quindi posso solo immaginare che stavo diventando pazzo. Ho modificato la mia risposta per riflettere ciò.
Whelkaholism

1
Haha, abbiamo tutti momenti del genere, vero? Guardiamo indietro al vecchio codice e diciamo "perché diavolo ho scritto questo?"
Joshua Walsh,

1

Sulla base delle altre risposte:

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 ° POSTO: risposta criptica quasi inutilmente. "Inutilmente": le dimensioni del codice / i vantaggi in termini di prestazioni non valgono i costi di leggibilità. "Cryptic": traduzione significativa necessaria per mappare ai requisiti "Layperson".
Tom,

0

FWIW, per MS-SQL, questa espressione farà il lavoro. Mantieni il primo WHEN ( WHEN num % 100 IN (11, 12, 13) THEN 'th') come il primo nell'elenco, poiché questo si basa sul fatto di essere provato prima degli altri.

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

Per Excel:

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

L'espressione (MOD(A1-11,100)>2)è TRUE (1) per tutti i numeri tranne che per la fine in 11,12,13(FALSE = 0). Quindi 2 * RIGHT(A1) * (MOD(A1-11,100)>2) +1)finisce come 1 per l'11 / 12/13, altrimenti:
1 restituisce 3
2 a 5,
3 a 7
altri: 9
- e i 2 caratteri richiesti vengono selezionati a "thstndrdth"partire da quella posizione.

Se vuoi davvero convertirlo abbastanza direttamente in SQL, questo ha funzionato per me per una manciata di valori di test:

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

Questa è l'implementazione darte può essere modificata in base alla lingua.

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

Mentre ci sono molte buone risposte qui, credo che ci sia spazio per un altro, questa volta basato sulla corrispondenza dei modelli, se non per altro, quindi almeno per una leggibilità discutibile

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

e cosa rende speciale questa soluzione? nient'altro che il fatto che sto aggiungendo alcune considerazioni sulle prestazioni per varie altre soluzioni

francamente dubito che la performance sia davvero importante per questo particolare scenario (che ha davvero bisogno degli ordinali di milioni di numeri) ma almeno emerge alcuni confronti da prendere in considerazione ...

1 milione di articoli per riferimento (il tuo millage può variare in base alle specifiche della macchina ovviamente)

con pattern matching e divisioni (questa risposta)

~ 622 ms

con pattern matching e stringhe (questa risposta)

~ 1967 ms

con due interruttori e divisioni (risposta accettata)

~ 637 ms

con un interruttore e divisioni (un'altra risposta)

~ 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

Un altro one-liner, ma senza confronti indicizzando solo il risultato regex in un array.

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

La versione di PowerShell può essere ulteriormente abbreviata:

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

0

Un altro 1 liner.

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

-2

Ecco la classe di estensione DateTime. Copia, incolla e divertiti

classe statica pubblica 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;
    }
}

Risultato:

9 ottobre 2014


Stai duplicando: a) la stringa del formato della data (X5) eb) l'intero resto del metodo (quando sorge il probabile caso d'uso (se non lo ha già fatto) che è necessario un suffisso ordinale per il non-giorno del mese scopi o anche un giorno del mese con una stringa di formato data diversa). Usa il metodo "OrdinalSuffix" che ho suggerito di essere esposto dalla risposta di Ian Warburton del 6 aprile 17 alle 16:32 ( stackoverflow.com/questions/20156/… ).
Tom,

-3

Un'altra alternativa che ho usato sulla base di tutti gli altri suggerimenti, ma non richiede involucri speciali:

    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";
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.