Tell me more ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

I would like to generate (as a return result of a function, or simply as the output of a program) the ordinal suffix of a positive integer concatenated to the number.

Samples:

1st  
2nd  
3rd  
4th  
...  
11th  
12th  
13th  
...  
20th  
21st
22nd
23rd
24th

And so on, with the suffix repeating the initial 1-10 subpattern every 10 until 100, where the pattern ultimately starts over.

The input would be the number and the output the ordinal string as shown above.

What is the smallest algorithm for this?

share|improve this question
Hi, NickC, and welcome to codegolf.SE! Just to clarify, do you mean that we should read a number like 11 as input, and output e.g. 11th? Is each number in the input on a separate line, and should the output numbers be on separate lines too? And do we need to handle more than one line of input? – Ilmari Karonen Jan 20 '12 at 18:26
1  
Are you looking for smallest algorithm or smallest code? – M42 Jan 20 '12 at 18:31
@Ilmari I am looking for 11 as input and 11th as output. I don't mind if it processes multiple lines but what I had in mind was processing just a single number. – NickC Jan 20 '12 at 21:00
@M42 You know, I'm not really sure. I don't have a strict requirement - but I was probably thinking smallest algorithm. – NickC Jan 20 '12 at 21:02

13 Answers

up vote 13 down vote accepted

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
share|improve this answer
1  
Should be able to drop the g off the substitution if you specify that each number must be on it's own line. Also, that'd let you change the word boundary to be $. But overall, +1, damn clever solution. – GigaWatt Jan 20 '12 at 21:05
@GigaWatt: Indeed, I wrote the code before NickC answered my question about the input format, so I made it as generic as possible. – Ilmari Karonen Jan 20 '12 at 21:42

Ruby, 60

It's not as good as the Perl entry, but I figured I'd work on my Ruby skills.

def o(n)n.to_s+%w{th st nd rd}[n/10%10==1||n%10>3?0:n%10]end

Function takes one integer argument, n, and returns a string as the ordinal form.

Works according to the following logic:
If the tens digit is a 1 or the ones digit is greater than 3 use the suffix 'th'; otherwise find the suffix from the array ['th', 'st', 'nd', 'rd'] using the final digit as the index.

share|improve this answer
o(113) is "113rd", should be "113th". The tens digit check doesn't account for numbers with more than two digits. – hammar Jan 20 '12 at 23:07
Ok, pitched in another %10 to compensate. Added 3 characters. (I feel like %10 appears enough where it should be shortened somehow, but I can't think of a solution) – GigaWatt Jan 20 '12 at 23:12
You can never beat Perl in writing horrible incomprehensible code that's as short as possible :) – jamylak Apr 6 at 10:25

Python, 68 characters

i=input()
k=i%10
print"%d%s"%(i,"tsnrhtdd"[(i/10%10!=1)*(k<4)*k::4])
share|improve this answer

Golfscript, 34 characters

~.10/10%1=!1$10%*.4<*'thstndrd'2/=
share|improve this answer

Mathematica 39

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

Usage

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

31st

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,

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

share|improve this answer
I'm confused; where is p defined? EDIT: never mind, I see how you're using this; unfortunately it does not work on my system. :-/ – Mr.Wizard Apr 6 at 13:13
It works on v.9.0.1. (I seem to recall that you are using 7.0.) Yes, p does not need to be defined. – David Carraher Apr 6 at 13:29
I understand that now. However, in v7 I get from SpokenString @ p[[117]] the output " part 117 of p". – Mr.Wizard Apr 6 at 13:31
So SpokenString gets revised from time to time. I wouldn't be surprised it this code (codegolf.stackexchange.com/questions/8859/…) also doesn't work on v. 7. BTW, it wasn't meant to be an enduring solution. – David Carraher Apr 6 at 13:37
Hah, I hadn't seen that answer before. – Mr.Wizard Apr 6 at 13:44
show 1 more comment

Ocaml

I'm pretty new to ocaml, but this is the shortest i could get.

let n x =
   let v = x mod 100 and string_v = string_of_int x in
   let z = v mod 10 in
   if v=11 || v=12 || v=13 then string_v^"th" 
   else if v = 1 || z = 1 then string_v^"st" else if v = 2 || z = 2 then string_v^"nd" else if v = 3 || z = 3 then string_v^"rd" else string_v^"th";;

I created a function n that takes a number as a parameter and does the work. Its long but thought it'd be great to have a functional example.

share|improve this answer
input: 11 would yield output: 11st – HorusKol Jan 23 '12 at 2:04
yeah, you're right.. I've made the correction. Thanks – Joseph Elcid Feb 7 '12 at 15:42
I've just tidied up the formatting on your code. Each line needs to be preceded by four spaces in order to be recognized as a code block. – Gareth Feb 7 '12 at 16:29
Wouldn't your first if check be shorter as if v>10 && v<14? I'm not familiar with ocaml, but is it necessary to have the string_v variable be so long? – Gaffi Apr 29 '12 at 3:49
No its not necessary, I could have chosen w or x. Just wanted something meaningful. But you're right, it would have made the code a little shorter. – Joseph Elcid May 11 '12 at 13:32

PHP, 151

I know that this program is not comparable to the others. Just felt like giving a solution.

<?$s=explode(' ',trim(fgets(STDIN)));foreach($s as$n){echo$n;echo(int)(($n%100)/10)==1?'th':($n%10==1?'st':($n%10==2?'nd':($n%10==3?'rd':'th')))."\n";}
share|improve this answer
you can save a few characters by using foreach($s as $n){echo$n; – karthik Apr 29 '12 at 11:24
@karthik yaa! thanks :) – l0n3_sh4rk Apr 29 '12 at 14:14

Python 2.7, 137 chars

def b(n):
 for e,o in[[i,'th']for i in['11','12','13']+list('4567890')]+[['2','nd'],['1','st'],['3','rd']]:
  if n.endswith(e):return n+o

n should be a string

I know I'm beaten by the competition here already, but I thought I'd provide my idea anyways

this just basically generates a list of key, value pairs with number (as a string) ending e and the ordinal o. It attempts to match 'th' first (hence why I didn't use a dictionary), so that it won't accidentally return 'st', for example, when it should be 'th'. This will work for any positive integer

share|improve this answer

PowerShell, 92

process{"$_$(switch -r($_){"(?<!1)1$"{'st'}"(?<!1)2$"{'nd'}"(?<!1)3$"{'rd'}default{'th'}})"}

Works with one number per line of input. Input is given through the pipeline. Making it work for only a single number doesn't reduce the size.

share|improve this answer

Scala 86

def x(n:Int)=n+{if(n%100/10==1)"th"else(("thstndrd"+"th"*6).sliding(2,2).toSeq(n%10))}

Scala 102:

def x(n:Int)=if((n%100)/10==1)n+"th"else if(n%10<4)n+("thstndrd".take(n+1)%5*2.drop(n%5*2))else n+"th"

102 as well:

def x(n:Int)=if((n%100)/10==1)n+"th"else if(n%10<4)n+("thstndrd".sliding(2,2).toSeq(n%10))else n+"th"

ungolfed:

def x (n: Int) =
  n + { if (((n % 100) / 10) == 1) "th" 
        else (("thstndrd"  + ("th"  * 6)).sliding (2, 2).toSeq (n % 10))
      }
share|improve this answer

C: 95 characters

A ridiculously long solution:

n;main(){scanf("%d",&n);printf("%d%s",n,n/10%10-1&&(n=n%10)<4&&n?n>2?"rd":n<2?"st":"nd":"th");}

It needs to be mangled more.

share|improve this answer
A user flagged this saying "is incorrect: doesn't loop at all, i.e. it works just for a given number, not _all_ numbers below it. see ideone.com/pO6UwV ." That's not a reason to flag as moderators do not judge correctness, but it may be an issue. – dmckee Apr 11 at 0:00
Based upon this comment from the question creator: "@Ilmari I am looking for 11 as input and 11th as output. I don't mind if it processes multiple lines but what I had in mind was processing just a single number. – NickC Jan 20 '12 at 21:00" it is doing what it should. I.e. it only should process a single number. – Fors Apr 11 at 4:42
sorry for that flag; I misread the requirement, and didn't have enough rep at the time, to comment directly. Sorry again. :) – Will Ness Apr 13 at 19:48

Javascript, 75

function o(s){return s+((0|s/10%10)==1?"th":[,"st","nd","rd"][s%10]||"th")}
share|improve this answer

Haskell, 95 chars

h=foldr g"th".show
g '1'"th"="1st"
g '2'"th"="2nd"
g '3'"th"="3rd"
g '1'[x,_,_]='1':x:"th"
g a s=a:s

Testing:

*Main> map h [1..40]
["1st","2nd","3rd","4th","5th","6th","7th","8th","9th","10th","11th","12th","13t
h","14th","15th","16th","17th","18th","19th","20th","21st","22nd","23rd","24th",
"25th","26th","27th","28th","29th","30th","31st","32nd","33rd","34th","35th","36
th","37th","38th","39th","40th"]

Must be loaded with -XNoMonomorphismRestriction.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.