¿Hay alguna manera fácil de crear ordinales en C #?

¿Hay alguna manera fácil en C # de crear ordinales para un número? Por ejemplo:

  • 1 vuelve primero
  • 2 devuelve 2do
  • 3 vuelve 3ro
  • … etc

¿Se puede hacer esto a través de String.Format() o hay funciones disponibles para hacer esto?

Esta página le brinda una lista completa de todas las reglas de formato numérico personalizadas:

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

Como puede ver, no hay nada allí sobre los ordinales, por lo que no se puede hacer usando String.Format. Sin embargo, no es tan difícil escribir una función para hacerlo.

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

Actualización: Técnicamente, los ordinales no existen para <= 0, así que actualicé el código anterior. También eliminó los métodos ToString () redundantes.

También tenga en cuenta que esto no está internacionalizado. No tengo idea de cómo se ven los ordinales en otros idiomas.

Recuerde la internacionalización!

Las soluciones aquí solo funcionan para inglés. Las cosas se vuelven mucho más complejas si necesitas soportar otros idiomas.

Por ejemplo, en español, “1st” se escribiría como “1.o”, “1.a”, “1.os” o “1.as” dependiendo de si lo que está contando es masculino, femenino o plural !

Entonces, si su software necesita soportar diferentes idiomas, intente evitar los ordinales.

Mi versión de la versión de Jesse de las versiones de Stu y Samjudson 🙂

Prueba unitaria incluida para mostrar que la respuesta aceptada es incorrecta cuando el número <1

  ///  /// Get the ordinal value of positive integers. ///  ///  /// 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 ///  /// The number. /// Ordinal value of positive integers, or  if less than 1. 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()); } 

Tendrás que hacer el tuyo. Desde lo alto de mi cabeza:

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

Entonces puedes hacer

 Console.WriteLine(432.Ordinal()); 

Editado para el 12/12/13 excepciones. LO DIGO desde lo más alto de mi cabeza 🙂

Editado para 1011: otros ya lo arreglaron, solo quiero asegurarme de que otros no agarren esta versión incorrecta.

Me gustaron más los elementos de las soluciones de Stu y de Samjudson y los trabajé juntos en lo que creo que es un combo utilizable:

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

Simple, limpio, rápido

  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 mejor aún, como un método de extensión

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

Ahora puedes simplemente llamar

 int a = 1; a.DisplayWithSuffix(); 

o incluso tan directo como

 1.DisplayWithSuffix(); 

Si bien aún no he realizado una evaluación comparativa de este punto, usted debería poder obtener un mejor rendimiento al evitar todas las declaraciones de casos condicionales.

Esto es java, pero un puerto para C # es 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(); } } 

Tenga en cuenta que la reducción de condicionales y el uso de la matriz de búsqueda deberían acelerar el rendimiento si genera muchos ordinales en un ciclo cerrado. Sin embargo, también reconozco que esto no es tan legible como la solución de statement de caso.

Similar a la solución de Ryan, pero aún más básica, simplemente uso una matriz simple y uso el día para buscar el ordinal correcto:

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

No he tenido la necesidad, pero supongo que podría usar una matriz multidimensional si quisiera tener soporte en múltiples idiomas.

Por lo que recuerdo de mis días de Uni, este método requiere un mínimo esfuerzo del servidor.

  private string getOrd(int num) { return $"{num}{(Range(11,3).Any(n => n == num % 100) ^ Range(1, 3).All(n => n != num%10) ? "th" : new[] { "st", "nd", "rd" }[num % 10-1])}"; } 

Si alguien busca un trazador de líneas: p

Yo uso esta clase de extensión:

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

Pidió la versión “menos redundante” de la respuesta de 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); } 

FWIW, para MS-SQL, esta expresión hará el trabajo. Mantenga el primer CUÁNDO ( WHEN num % 100 IN (11, 12, 13) THEN 'th' ) como el primero en la lista, ya que esto depende de haber sido probado antes que los demás.

 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 

Para Excel:

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

La expresión (MOD(A1-11,100)>2) es VERDADERO (1) para todos los números excepto para cualquier terminación en 11,12,13 (FALSO = 0). Entonces 2 * RIGHT(A1) * (MOD(A1-11,100)>2) +1) termina como 1 para 11/12/13, de lo contrario:
1 evalúa a 3
2 a 5,
3 a 7
otros: 9
– y los 2 caracteres requeridos se seleccionan de "thstndrdth" comenzando desde esa posición.

Si realmente quiere convertir eso bastante directamente a SQL, esto funcionó para mí para un puñado de valores de prueba:

 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 ) 

EDITAR : Como señala YM_Industries en el comentario, la respuesta de samjudson sí funciona para números superiores a 1000, el comentario de nickf parece haber desaparecido, y no recuerdo cuál fue el problema que vi. Dejó esta respuesta aquí para los tiempos de comparación.

Una gran cantidad de estos no funcionan para números> 999, como señaló nickf en un comentario (EDITAR: ahora falta).

Aquí hay una versión basada en una versión modificada de la respuesta aceptada de samjudson que sí lo hace.

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

También la respuesta de Shahzad Qureshi usando la manipulación de cadenas funciona bien, sin embargo, tiene una penalización de rendimiento. Para generar muchos de estos, un progtwig de ejemplo de LINQPad hace que la versión de cadena sea 6-7 veces más lenta que esta entera (aunque tendrías que generar mucho para notar).

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

Basado en las otras respuestas:

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

Aquí está la clase de extensión DateTime. Copiar, pegar y disfrutar

clase estática pública 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; } } 

Resultado:

9 de octubre de 2014

Otra alternativa que utilicé en base a todas las otras sugerencias, pero no requiere una carcasa especial:

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