Is there an easy way in C# to create Ordinals for a number? For example:
- 1 returns 1st
- 2 returns 2nd
- 3 returns 3rd
- ...etc
Can this be done through String.Format()
or are there any functions available to do this?
Is there an easy way in C# to create Ordinals for a number? For example:
Can this be done through String.Format()
or are there any functions available to do this?
You'll have to roll your own. From the top of my head:
public static string Ordinal(this int number)
{
var work = number.ToString();
if (number == 11 || number == 12 || number == 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;
}
You can then do
Console.WriteLine(432.Ordinal());
Edited for 11/12/13 exceptions. I DID say from the top of my head :-)
How's this?
http://csharpaspnet.blogspot.com/2007/01/ordinal-numbers-in-c-like-1-as-1st-3-as.html
@Stu - love the elegance of making an extension method out of this.
This page gives you a complete listing of all custom numerical formatting rules:
http://msdn.microsoft.com/en-us/library/0c899ak8.aspx
As you can see, there is nothing in there about ordinals, so it can't be done using String.Format. However its not really that hard to write a function to do it.
public string AddOrdinal(int num)
{
switch(num % 100)
{
case 11:
case 12:
case 13:
return num.ToString() + "th";
}
switch(num % 10)
{
case 1:
return num.ToString() + "st";
case 2:
return num.ToString() + "nd";
case 3:
return num.ToString() + "rd";
default:
return num.ToString() + "th";
}
}
I rather liked elements from both Stu's and samjudson's solutions and worked them together into what I think is a usable combo:
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;
}
}
While I haven't benchmarked this yet, you should be able to get better performance by avoiding all the conditional case statements.
This is java, but a port to C# is 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();
}
}
Note, the reduction of conditionals and the use of the array lookup should speed up performance if generating a lot of ordinals in a tight loop. However, I also concede that this isn't as readable as the case statement solution.
Remember internationalisation!
The solutions here only work for English. Things get a lot more complex if you need to support other languages.
For example, in Spanish "1st" would be written as "1.o", "1.a", "1.os" or "1.as" depending on whether the thing you're counting is masculine, feminine or plural!
So if your software needs to support different languages, try to avoid ordinals.
My version of Jesse's version of Stu's and samjudson's versions :)
Included unit test to show that the accepted answer is incorrect when number < 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());
}