c# - que - poner numeros ordinales en excel
¿Hay alguna manera fácil de crear ordinales en C#? (17)
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
¿Hay alguna manera fácil en C # de crear Ordinals 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?
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";
}
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.
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
)
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;
}
}
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
/// <summary>
/// Get the ordinal value of positive integers.
/// </summary>
/// <remarks>
/// Only works for english-based cultures.
/// Code from: http://.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());
}
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";
}
}
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);
}
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.
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 declaración 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.
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();
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.
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");
}
}
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 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 answer usando la manipulación de cadenas funciona bien, sin embargo, tiene una penalización de rendimiento. Para generar muchos de estos, un programa 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";
}
}
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
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";
}