13
$\begingroup$

Is there a built-in way to convert an integer into its ordinal string representation (as per this page). That is, something akin to

{1 -> "1st", 2 -> "2nd", (*etc...*)}

or

{1 -> "First", 2 -> "Second", (*etc...*)}
$\endgroup$
  • $\begingroup$ I have now migrated my answer to this post (see below). In short: As of Version 10 there is IntegerName but it needs to be tweaked to give what you/we want. $\endgroup$ – gwr Jan 31 at 11:25
16
$\begingroup$

The coolest way is to check the answer to this question by David Carraher. I am shamelessly stealing his code here to write a function that gives you rules for up to maxNumber:

ordinalRule[maxNumber_Integer] /; maxNumber > 0 := 
  Block[{p},
    Thread[
      Function[{x}, 
        x -> StringSplit[SpokenString[p[[#]]]][[2]] &[x] // Quiet] /@ Range[maxNumber]
  ]];

For example:

ordinalRule[100]

(*out*){1 -> "1st", 2 -> "2nd", 3 -> "3rd", 4 -> "4th", 5 -> "5th", 
 6 -> "6th", 7 -> "7th", 8 -> "8th", 9 -> "9th", 10 -> "10th", 
 11 -> "11th", 12 -> "12th", 13 -> "13th", 14 -> "14th", 15 -> "15th",
  16 -> "16th", 17 -> "17th", 18 -> "18th", 19 -> "19th", 
 20 -> "20th", 21 -> "21st", 22 -> "22nd", 23 -> "23rd", 24 -> "24th",
  25 -> "25th", 26 -> "26th", 27 -> "27th", 28 -> "28th", 
 29 -> "29th", 30 -> "30th", 31 -> "31st", 32 -> "32nd", 33 -> "33rd",
  34 -> "34th", 35 -> "35th", 36 -> "36th", 37 -> "37th", 
 38 -> "38th", 39 -> "39th", 40 -> "40th", 41 -> "41st", 42 -> "42nd",
  43 -> "43rd", 44 -> "44th", 45 -> "45th", 46 -> "46th", 
 47 -> "47th", 48 -> "48th", 49 -> "49th", 50 -> "50th", 51 -> "51st",
  52 -> "52nd", 53 -> "53rd", 54 -> "54th", 55 -> "55th", 
 56 -> "56th", 57 -> "57th", 58 -> "58th", 59 -> "59th", 60 -> "60th",
  61 -> "61st", 62 -> "62nd", 63 -> "63rd", 64 -> "64th", 
 65 -> "65th", 66 -> "66th", 67 -> "67th", 68 -> "68th", 69 -> "69th",
  70 -> "70th", 71 -> "71st", 72 -> "72nd", 73 -> "73rd", 
 74 -> "74th", 75 -> "75th", 76 -> "76th", 77 -> "77th", 78 -> "78th",
  79 -> "79th", 80 -> "80th", 81 -> "81st", 82 -> "82nd", 
 83 -> "83rd", 84 -> "84th", 85 -> "85th", 86 -> "86th", 87 -> "87th",
  88 -> "88th", 89 -> "89th", 90 -> "90th", 91 -> "91st", 
 92 -> "92nd", 93 -> "93rd", 94 -> "94th", 95 -> "95th", 96 -> "96th",
  97 -> "97th", 98 -> "98th", 99 -> "99th", 100 -> "100th"}

I don't know whether this counts as built-in though.

----EDIT----

From @Wreach's comment: there is an undocumented built-in (that SpokenString calls when dealing with numbers to ordinals) that does exactly that:

Speak["stackexchange"];
SpokenStringDump`SpeakOrdinal[121]
(*out*) 121st

In v10 I need to call Speak before the relevant function auto-loads.

$\endgroup$
  • 2
    $\begingroup$ Might not be completely builtin, but still very neat. Nice find! $\endgroup$ – Daniel Nov 4 '14 at 15:40
  • 9
    $\begingroup$ +1 It uses an undocumented internal function like this: SpokenStringDump`SpeakOrdinal[1] returning "1st". You might have to reference the symbol Speak first to make sure that the package is auto-loaded. $\endgroup$ – WReach Nov 4 '14 at 18:36
10
$\begingroup$

I noticed only afterwards that you were asking for a built-in method. I'm not certain if you would actually want to rely on an undocumented feature instead of writing your own, since it's not hard.

This requires v10 for StringTemplate. It could be replaced with something else, of course...

Range[120] /.
 { tens_Integer /; Floor[Mod[tens, 100], 10] == 10 :> StringTemplate["`1`th"][tens],
   firsts_Integer /; Mod[firsts, 10] == 1 :> StringTemplate["`1`st"][firsts],
   seconds_Integer /; Mod[seconds, 10] == 2 :> StringTemplate["`1`nd"][seconds],
   thirds_Integer /; Mod[thirds, 10] == 3 :> StringTemplate["`1`rd"][thirds],
   rest_Integer :> StringTemplate["`1`th"][rest] }

(* { "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", "31st", "32nd", "33rd",
     "34th", "35th", "36th", "37th", "38th", "39th", "40th", "41st",
     "42nd", "43rd", "44th", "45th", "46th", "47th", "48th", "49th",
     "50th", "51st", "52nd", "53rd", "54th", "55th", "56th", "57th",
     "58th", "59th", "60th", "61st", "62nd", "63rd", "64th", "65th",
     "66th", "67th", "68th", "69th", "70th", "71st", "72nd", "73rd",
     "74th", "75th", "76th", "77th", "78th", "79th", "80th", "81st",
     "82nd", "83rd", "84th", "85th", "86th", "87th", "88th", "89th",
     "90th", "91st", "92nd", "93rd", "94th", "95th", "96th", "97th",
     "98th", "99th", "100th", "101st", "102nd", "103rd", "104th", 
     "105th", "106th", "107th", "108th", "109th", "110th", "111th",
     "112th", "113th", "114th", "115th", "116th", "117th", "118th",
     "119th", "120th" } *)
$\endgroup$
6
$\begingroup$

A brute force but very simple approach (no undocumented functions needed) that I think will work with any version of Mathematica.

ordinalRule[n_Integer /; Mod[n, 100] == 11] := generalRule[n]
ordinalRule[n_Integer /; Mod[n, 100] == 12] := generalRule[n]
ordinalRule[n_Integer /; Mod[n, 100] == 13] := generalRule[n]
ordinalRule[n_Integer /; Mod[n, 10] == 1] = specialRule1[n];
ordinalRule[n_Integer /; Mod[n, 10] == 2] = specialRule2[n];
ordinalRule[n_Integer /; Mod[n, 10] == 3] = specialRule3[n];
ordinalRule[n_Integer] = generalRule[n];
specialRule1[n_] := n -> (ToString[n] <> "st")
specialRule2[n_] := n -> (ToString[n] <> "nd")
specialRule3[n_] := n -> (ToString[n] <> "rd")
generalRule[n_] := n -> (ToString[n] <> "th")

ordinalRule /@ Range @ 130
{1 -> "1st", 2 -> "2nd", 3 -> "3rd", 4 -> "4th", 5 -> "5th", 
 6 -> "6th", 7 -> "7th", 8 -> "8th", 9 -> "9th", 10 -> "10th", 
 11 -> "11th", 12 -> "12th", 13 -> "13th", 14 -> "14th", 15 -> "15th", 
 16 -> "16th", 17 -> "17th", 18 -> "18th", 19 -> "19th", 20 -> "20th", 
 21 -> "21st", 22 -> "22nd", 23 -> "23rd", 24 -> "24th", 25 -> "25th", 
 26 -> "26th", 27 -> "27th", 28 -> "28th", 29 -> "29th", 30 -> "30th", 
 31 -> "31st", 32 -> "32nd", 33 -> "33rd", 34 -> "34th", 35 -> "35th", 
 36 -> "36th", 37 -> "37th", 38 -> "38th", 39 -> "39th", 40 -> "40th", 
 ...
 91 -> "91st", 92 -> "92nd", 93 -> "93rd", 94 -> "94th", 95 -> "95th", 
 96 -> "96th", 97 -> "97th", 98 -> "98th", 99 -> "99th", 100 -> "100th", 
 101 -> "101st", 102 -> "102nd", 103 -> "103rd", 104 -> "104th", 105 -> "105th", 
 106 -> "106th", 107 -> "107th", 108 -> "108th", 109 -> "109th", 110 -> "110th", 
 111 -> "111th", 112 -> "112th",  113 -> "113th", 114 -> "114th", 115 -> "115th", 
 116 -> "116th", 117 -> "117th", 118 -> "118th", 119 -> "119th", 120 -> "120th", 
 121 -> "121st", 122 -> "122nd", 123 -> "123rd", 124 -> "124th", 125 -> "125th", 
 126 -> "126th", 127 -> "127th", 128 -> "128th", 129 -> "129th", 130 -> "130th"}
$\endgroup$
  • $\begingroup$ Ah, <> for string concatenation! I forgot it and just used templating. :) $\endgroup$ – kirma Nov 4 '14 at 19:24
4
$\begingroup$
ordinalizeF = Module[{mod = Boole[!MemberQ[{11, 12}, Mod[Abs[#], 100]] ] Mod[Abs[#], 10]}, 
   With[{suffix = Switch[mod, 1, "st", 2, "nd", 3, "rd", _, "th"]}, Row[{ToString@#, suffix}]]] &;

ordinalizeF /@ {1, 2, 3, 11, -12, 21, 33, 5542}
(* {1st, 2nd, 3rd, 11th, -12th, 21st, 33rd, 5542nd} *)

Thread[# -> ordinalizeF /@ #] &@{1, 2, 3, 11, -12, 21, 33, 542}
(*{1 -> 1st, 2 -> 2nd, 3 -> 3rd, 11 -> 11th, -12 -> -12th, 21 -> 21st, 33 -> 33rd, 542-> 542nd} *)
$\endgroup$
3
$\begingroup$

Short answer: As of Version 10 there is IntegerName, but it has to be slightly modified to be really helpful for ordinal numbers.

Building a converter function

IntegerName allows to provide qualifiers and combinations of these. Unfortunately, using "Ordinal" will not give a correctly spelled result since there is no "and" as in "one hundred and first" (cf. here).

Furthermore, you cannot obtain a "DigitsWords"-like output (e.g. "101st" ) for ordinal numbers by doing IntegerName[ 101, {"DigitsWords", "Ordinal" }] or IntegerName[ 101, {"Ordinal", "DigitsWords" } ] even though the documentation looks like combining qualifiers for IntegerName in this way should be possible.

But tweaking IntegerName slightly within a custom build converter function will get us what we want:

IntegerNameOrdinal[ i_Integer ] := With[
    { 
       stringPatternTwoDigits = IntegerName[ Range[1, 99], "Ordinal"]
    }   
    ,
    IntegerName[ i, "Ordinal" ] // StringReplace[
      ( a__ /; StringFree[a, "-"] ) ~~ b:stringPatternTwoDigits :> a ~~ "and " ~~ b
    ]
]

IntegerNameOrdinal[ i_Integer, "DigitsWords" ] := Module[
    {
        strEnding = IntegerName[ i, "Ordinal" ] // StringTake[ # ,-2 ]&,
        strDigits = ToString @ i
    }
    ,
    strDigits ~~ strEnding
]

IntegerNameOrdinal[ i_Integer, ___ ] := IntegerNameOrdinal[ i ]

Now, we get:

IntegerNameOrdinal[ 2000232, "Words" ] (* or simply: IntegerNameOrdinal[ 2000232 ] *)

"two million two hundred and thirty-second"

IntegerNameOrdinal[ #, "DigitsWords" ]& /@ Range[ 100 ]

{"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", "31st", "32nd", "33rd", "34th", "35th", "36th", "37th", "38th", "39th", "40th", "41st", "42nd", "43rd", "44th", "45th", "46th", "47th", "48th", "49th", "50th", "51st", "52nd", "53rd", "54th", "55th", "56th", "57th", "58th", "59th", "60th", "61st", "62nd", "63rd", "64th", "65th", "66th", "67th", "68th", "69th", "70th", "71st", "72nd", "73rd", "74th", "75th", "76th", "77th", "78th", "79th", "80th", "81st", "82nd", "83rd", "84th", "85th", "86th", "87th", "88th", "89th", "90th", "91st", "92nd", "93rd", "94th", "95th", "96th", "97th", "98th", "99th", "100th"}

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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