Outputting ordinal numbers (1st, 2nd, 3rd)

Python 2, 49 bytes

lambda n:`n`+'tsnrhtdd'[n%5*(n%100^15>4>n%10)::4]

An anonymous function. A full program would be counted at 55 bytes.

'tsnrhtdd'[i::4] encodes the suffixes th st nd rd for values of i from 0 to 3. Given this, all we need is a way to map the values of n to the index of the corresponding suffix, i. A straightforward expression that works is (n%10)*(n%10<4 and 10<n%100<14). We can easily shorten this by dropping the first set of parentheses and observing that n%5 gives the same results as n%10 for the values of n with the special suffixes. With a bit of trial and error, one may also shorten 10<n%100<14 to n%100^15>4, which can be chained with the other conditional to save even more bytes.


Perl, 37 + 1 characters

s/1?\d\b/$&.((0,st,nd,rd)[$&]||th)/eg

This is a regexp substitution that appends the appropriate ordinal suffix to any numbers in $_ that are not already followed by a letter. To apply it to file input, use the p command line switch, like this:

perl -pe 's/1?\d\b/$&.((0,st,nd,rd)[$&]||th)/eg'

This is a complete Perl program that reads input from stdin and writes the processed output to stdout. The actual code is 37 chars long, but the p switch counts as one extra character.

Sample input:

This is the 1 line of the sample input...
...and this is the 2.
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
101 102 103 104 105 106 107 108 109 110

Output:

This is the 1st line of the sample input...
...and this is the 2nd.
1st 2nd 3rd 4th 5th 6th 7th 8th 9th 10th
11th 12th 13th 14th 15th 16th 17th 18th 19th 20th
21st 22nd 23rd 24th 25th 26th 27th 28th 29th 30th
101st 102nd 103rd 104th 105th 106th 107th 108th 109th 110th

Numbers already followed by letters will be ignored, so feeding the output again through the filter won't change it. Spaces, commas and periods between numbers are not treated specially, so they're assumed to separate numbers like any other punctuation. Thus, e.g. 3.14159 becomes 3rd.14159th.

How does it work?

  • First, this is a global regexp replacement (s///g). The regexp being matched is 1?\d\b, where \d matches any digit and \b is a zero-width assertion matching the boundary between an alphanumeric and a non-alphanumeric character. Thus, 1?\d\b matches the last digit of any number, plus the previous digit if it happens to be 1.

  • In the substitution, which is evaluated as Perl code due to the /e switch, we take the matched string segment ($&) and append (.) to it the suffix obtained by using $& itself as an integer index to the list (0,st,nd,rd); if this suffix is zero or undefined (i.e. when $& is zero or greater than three), the || operator replaces it with th.


Edit: If the input is restricted to a single integer, then this 35 character solution will suffice:

s/1?\d$/$&.((0,st,nd,rd)[$&]||th)/e

Mathematica 39 45 bytes

Note: In recent versions of Mathematica, asking for the nth part of p, where p is undefined, generates an error message, but returns the correct answer anyway. I've added Quiet to prevent the error message from printing.

Quiet@StringSplit[SpokenString[p[[#]]]][[2]]&

Usage

Quiet@StringSplit[SpokenString[p[[#]]]][[2]] &[31]

31st

Quiet@StringSplit[SpokenString[p[[#]]]][[2]] &/@Range[21]

{"1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th", "20th", "21st"}


How it works

SpokenString writes out an any valid Mathematica expression as it might be spoken. Below are two examples from the documentation for SpokenString,

SpokenString[Sqrt[x/(y + z)]]

"square root of the quantity x over the quantity y plus z" *)

SpokenString[Graphics3D[Sphere[{{0, 0, 0}, {1, 1, 1}}]], "DetailedGraphics" -> True]

"a three-dimensional graphic consisting of unit spheres centered at 0, 0, 0 and 1, 1, 1"


Now, for the example at hand,

Quiet@SpokenString[p[[#]]] &[31]

"the 31st element of p"

Let's represent the above string as a list of words:

StringSplit[%]

{"the", "31st", "element", "of", "p"}

and take the second element...

%[[2]]

31st