# Description :

Given a string as input, check if it is a valid ordinal number in English or not. If it is valid return truthy value otherwise return falsy value. (Suggested by @Arnauld. Thanks . Also by @JoKing)

For users who want to know about ordinal numbers go here :

https://www.mathsisfun.com/numbers/cardinal-ordinal-chart.html (Suggestion by : qwr)

# Possible inputs :

21st ---> true
12nd ---> false
1nd ---> false
....


This is a code golf challenge so shortest code in each language will be the winner.

# Examples :

console.log('12th' , true) // This evaluates to true
console.log('1st' , true) // also evaluates to true
console.log('21nd' , false) // returns false
console.log('11st' , false) // returns false
console.log('111199231923819238198231923213123909808th' , true) // true

Since a lot of people asked the question regarding whether input will be only valid strings or not :

All inputs will always be valid. i.e they will be in the form of string and consist of a digit (or number of digits) along with one of the four suffixes :

st , nd , rd , th

• Can you clarify the rules of ordinal numbers? Or at least put a link to what the rules you are following. – qwr Apr 14 '18 at 17:00
• They are normal rules. I changed nothing. But thanks for the input , I added a link – Muhammad Salman Apr 14 '18 at 17:02
• @Jonathan Allan Ordinal numbers start from 1st, negative ordinals do not exist - english.stackexchange.com/questions/309713/… – Oliver Ni Apr 14 '18 at 23:18
• @JonathanAllan OP says "Input is going to be valid ordinal pattern." which means no negatives – Oliver Ni Apr 15 '18 at 5:12
• You say the inputs will always be valid but I think a better term would be well formed. Both 12th and 12nd are well formed but only the former is valid. – David Conrad Apr 20 '18 at 21:05

# Python,  56  53 bytes

-3 thanks to (use unique letter inclusion instead of penultimate character equality)

lambda v:'hsnrhhhhhh'[(v[-4:-3]!='1')*int(v[-3])]in v


An unnamed function.

Try it online!

### How?

Since all input (here v) is guaranteed to be of the form \d*[st|nd|rd|th] we can just test whether a character exists in v which we expect to be there if it were correct (s, n, r, or h, respectively) - that is <getExpectedLetter>in v.

The last digit usually determines this:

v[-3]: 0 1 2 3 4 5 6 7 8 9
v[-2]: h s n r h h h h h h


...except when the penultimate digit is a 1, when all should end with th and hence our expected character must be h; to evaluate this we can take a slice (to avoid an index error occurring for inputs with no -4th character) v[-4:-3]. Since 0 maps to h already we can achieve the desired effect using multiplication prior to indexing into 'hsnrhhhhhh'.

• st, nd, rd and th all have a unique letter so you can just test if it occurs in the string 53 bytes – Asone Tuhid Apr 17 '18 at 16:14
• @AsoneTuhid nice golf - thanks! – Jonathan Allan Apr 17 '18 at 16:41
• @AsoneTuhid - also saved three on my Jelly answer, so double the thanks! – Jonathan Allan Apr 17 '18 at 16:47

# Bash + GNU utilities, 54

Regex matching seems to be a straightforward way to go. I'm pretty sure this expression could be shortened more:

egrep '((^|[^1])(1st|2nd|3rd)|(1.|(^|[^1])[^1-3])th)$'  Input from STDIN. Output as a shell return code - 0 is truthy and 1 is falsey. Try it online! • What ? this is not outputting the correct answer. – Muhammad Salman Apr 14 '18 at 16:58 • @MuhammadSalman That's because it's a test suite. Take a look at the exit codes for 1st and 1th. – Dennis Apr 14 '18 at 17:09 • egrep is capable of addition and primality testing (in unary), so I think you can make this an egrep answer. – Dennis Apr 14 '18 at 17:10 • I am sorry but my bash sucks as in I have no idea about a thing in it. I got bored so used a diff checker to check for difference between input and output. I see your point. So I have one question now @Dennis : Does Bash have booleans ? – Muhammad Salman Apr 14 '18 at 17:13 • Maybe the test cases would be more clear if egrep would be executed separately for each input to get matching exit code for each: Try it online!. – manatwork Apr 14 '18 at 17:15 this is under the assumption that the input is valid ordinal pattern. if its not the case changes need to be made # JavaScript (Node.js),9792 78 bytes s=>("tsnr"[~~((n=(o=s.match(/(\d{1,2})(\D)/))[1])/10%10)-1?n%10:0]||'t')==o[2]  Try it online! ### Explanation s=> ("tsnr" // all the options for ordinal - 4-9 will be dealt afterwards [~~( //floor the result of the next expression (n=( //save the number (actually just the two right digits of it into n o=s.match(/(\d{1,2})(\D)/))[1]) //store the number(two digits) and the postfix into o (array) /10%10)-1 //if the right most(the tenths digit) is not 1 (because one is always 'th') ?n%10:0] //return n%10 (where we said 0-3 is tsnr and afterwards is th ||'t') // if the result is undefined than the request number was between 4 and 9 therefor 'th' is required ==o[2] // match it to the actual postfix  ### _____________________________________________________________________ port of @Herman Lauenstein # JavaScript (Node.js), 48 bytes s=>/1.th|(^|[^1])(1st|2nd|3rd|[^1-3]th)/.test(s)  Try it online! • If the assumption reg solution can also be ***. – l4m2 Apr 14 '18 at 20:12 • If not assumed it's /\d*(st|nd|rd|th)/ input, 1sta pass the reg test; if assumed, /1.th|(^|[^1])(1s|2n|3r|[^1-3]t)/ work – l4m2 Apr 14 '18 at 23:29 # Java 8, 54 51 bytes s->s.matches(".*1.th|(.*[^1])?(1s|2n|3r|[^1-3]t).")  Explanation: Try it online. s-> // Method with String parameter and boolean return-type s.matches(".*1.th|(.*[^1])?(1s|2n|3r|[^1-3]t).") // Validates if the input matches this entire regex  Java's String#matches implicitly adds ^...$.

Regex explanation:

^.*1.th|(.*[^1])?(1s|2n|3r|[^1-3]t).$^ Start of the regex .*1. If the number ends in 11-19: th it must have a trailing th | If not: (.* )? Optionally it has leading digits, [^1] excluding a 1 at the end (1s|2n|3r . followed by either 1st, 2nd, 3rd, |[^1-3]t). 0th, 4th, 5th, ..., 8th, or 9th$   End of the regex


# Pyth, 49 60 bytesSBCS

Js<2zK%J100I||qK11qK12qK13q>2z"th".?qz+J@c."dt8¸*£tÎðÎs"2J


Test suite

SE ate some unprintables in the code (and in the below explanation) but they're present in the link.

Explanation:
Js<2zK%J100I||qK11qK12qK13q>2z"th".?qz+J@c."dt8¸*£tÎðÎs"2J # Code
Js<2z                                                         # J= the integer in the input
K%J100                                                   # K=J%100
I||qJ11qJ12qJ13                                    # IF K is 11, 12, or 13:
q>2z"th"                            #  Print whether the end of the input is "th"
.?                          # Otherwise:
qz                        #  Print whether the input is equal to
+J                      #   J concatenated with
@                   J #    The object at the Jth modular index of
."dt8¸*£tÎðÎs"   #     The string "thstndrdthththththth"
c                 2  #      Chopped into strings of length 2 as a list

Python 3 translation:
z=input();J=int(z[:-2]);K=J%100
if K==11or K==12or K==13:print(z[-2:]=="th")
else:print(z==str(J)+["thstndrdthththththth"[2*i:2*i+2] for i in range(10)][J%10])


# Python 2, 928274 68 bytes

-8 thanks to Chas Brown
-6 thanks to Kevin Cruijssen

lambda s:(a+'t'*10+a*8)[int(s[-4:-2]):][:1]==s[-2:-1]
a='tsnr'+'t'*6


Constructs a big string of ths, sts, nds, and rds for endings 00 to 99. Then checks to see if it matches.

# Retina, 35 31 bytes

-4 bytes thanks to @Asone Tuhid

Thanks to @Leo for finding a bug

1.th|(^|[^1])(1s|2n|3r|[04-9]t)


Outputs 1 for true and 0 for false. This assumes the input is in ordinal format with a valid suffix (ends with st, nd, rd or th).

Try it online!

/..$/;say$&eq(th,st,nd,rd,(th)x6)[$_%100-$_%10-10&&$_%10]  Try it online! # Haskell, 100 bytes f('1':_:a@[_,_])=a=="th" f(a:b)|length b>2=f b f(a:"th")|a>'3'=1>0 f x=elem x$words"0th 1st 2nd 3rd"


Try it online!

# Jelly,  25  22 bytes

-3 bytes thanks to an observation made in a comment made by on my Python entry.

ḣ-2VDṫ-’Ạ×ɗ/«4ị“snrh”e


Try it online! Or see the test-suite.

### How?

ḣ-2VDṫ-’Ạ×ɗ/«4ị“snrh”e - Link: list of characters   e.g. "213rd" or "502nd" or "7th"
ḣ-2                    - head to index -2                "213"      "502"      "7"
V                   - evaluate                         213        502        7
D                  - cast to decimal list            [2,1,3]    [5,0,2]    [7]
ṫ-                - tail from index -1                [1,3]      [0,2]    [7]
/           - reduce with:                                          (no reduction since already length 1)
’               -     decrement (the left)           0         -1        x
Ạ              -     all? (0 if 0, 1 otherwise)     0          1        x
×             -     multiply (by the right)        0          2        x
«4         - minimum of that and 4              0          2        4
ị“snrh”  - index into "snrh"                 'h'        'n'      'h'
e - exists in? (the input list)        0          1        1


# Python 2, 94827773 70 bytes

lambda s:'tsnrthtddh'[min(4,int(s[-3])*(('0'+s)[-4]!='1'))::5]==s[-2:]


Try it online!

# 05AB1E, 24 bytes

0ìþR2£≠*.•’‘vê₅ù•sèsáнQ


Try it online! or as a Test suite

Explanation

0ì                         # prepend 0 to input
þ                        # remove letters
R                       # reverse
2£                     # take the first 2 digits
≠                   # check if the 2nd digit is false
*                  # and multiply with the 1st digit
.•’‘vê₅ù•         # push the string "tsnrtttttt"
sè       # index into this string with the number calculated
sáн    # get the first letter of the input
Q   # compare for equality


# Ruby, 42 39 bytes

Lambda:

->s{s*2=~/1..h|[^1](1s|2n|3r|[4-90]t)/}


Try it online!

User input:

p gets*2=~/1..h|[^1](1s|2n|3r|[4-90]t)/


Try it online!

Matches:

• 1(anything)(anything)h - 12th
• (not 1)1s - (1st)
• (not 1)2n - (2nd)
• (not 1)3r - (3rd)

Because [^1] (not 1) doesn't match the beginning of a string, the input is duplicated to make sure there's a character before the last.

# Ruby-n, 35 bytes

p~/1..h|([^1]|^)(1s|2n|3r|[4-90]t)/


Try it online!

Same idea as above but instead of duplicating the string, this also matches the start of the string (^).

# Excel, 63 bytes

=A1&MID("thstndrdth",MIN(9,2*RIGHT(A1)*(MOD(A1-11,100)>2)+1),2)


(MOD(A1-11,100)>2) returns FALSE when A1 ends with 11-13

2*RIGHT(A1)*(MOD(A1-11,100)>2)+1 returns 1 if it's in 11-13 and 3,5,7,etc. otherwise

MIN(9,~) changes any returns above 9 into 9 to pull the th from the string

MID("thstndrdth",MIN(~),2) pulls out the first th for inputs ending in 11-13, st for 1, nd for 2, rd for 3, and the last th for anything higher.

=A1&MID(~) prepends the original number to the ordinal.

Posting as wiki since I am not the author of this. (Source)

# Wolfram Language (Mathematica), 122 bytes

Unlike most of the other answers on here, this will actually return false when the input is not a "valid ordinal pattern", so it will correctly return false on input like "3a23rd", "monkey" or "╚§+!". So I think this works for the entire set of possible input strings.

StringMatchQ[((d=DigitCharacter)...~~"1"~(e=Except)~d~~(e["1"|"2"|"3",d]~~"th")|("1st"|"2nd"|"3rd"))|(d...~~"1"~~d~~"th")]


Try it online!

# Wolfram Language (Mathematica), 65 59 bytes

SpokenString@p[[#]]~StringTake~{5,-14}&@@ToExpression@#==#&


Try it online!

Of course Mathematica has a built-in (although undocumented) for converting to ordinal number. Source.

(for the 65-byte version: according to that it seems that v9 and before doesn't need calling Speak before so it may be possible to save some more bytes)

Also check out KellyLowder's answer for a non-builtin version.

# PHP, 60 bytes

boring: regexp once more the shortest solution

<?=preg_match("/([^1]|^)(1st|2nd|3rd|\dth)$|1\dth$/",$argn);  empty output for falsy, 1 for truthy. Run as pipe with -nF or try it online. (TiO wrapped as function for convenience) # x86 machine code, 65 bytes 00000000: 31c0 4180 3930 7cfa 8079 0161 7ef4 8079 1.A.90|..y.a~..y 00000010: ff31 7418 31db 8a19 83eb 308a 9300 0000 .1t.1.....0..... 00000020: 0031 db43 3851 010f 44c3 eb0a 31db 4380 .1.C8Q..D...1.C. 00000030: 7901 740f 44c3 c374 736e 7274 7474 7474 y.t.D..tsnrttttt 00000040: 74 t  Assembly: section .text global func func: ;the function uses fastcall conventions ;ecx=first arg to function (ptr to input string) xor eax, eax ;reset eax to 0 read_str: inc ecx ;increment ptr to string cmp byte [ecx], '0' jl read_str ;if the char isn't a digit, get next digit cmp byte [ecx+1], 'a' jle read_str ;if the char after the digit isn't a letter, get next digit cmp byte [ecx-1], '1' je tens ;10-19 have different rules, so jump to 'tens' xor ebx, ebx ;reset ebx to 0 mov bl, byte [ecx] ;get current digit and store in bl (low byte of ebx) sub ebx, 0x30 ;convert ascii digit to number mov dl, [lookup_table+ebx] ;get correct ordinal from lookup table xor ebx, ebx ;reset ebx to 0 inc ebx ;set ebx to 1 cmp byte [ecx+1], dl ;is the ordinal correct according to the lookup table? cmove eax, ebx ;if the ordinal is valid, set eax (return reg) to 1 (in ebx) jmp end ;jump to the end of the function and return tens: xor ebx, ebx ;reset ebx to 0 inc ebx ;set ebx to 1 cmp byte [ecx+1], 't' ;does it end in th? cmove eax, ebx ;if the ordinal is valid, set eax (return reg) to 1 (in ebx) end: ret ;return the value in eax section .data lookup_table db 'tsnrtttttt'  Try it online! # Perl-compatible regular expression, 29 bytes 1.th|(?<!1)(1s|2n|3r)|[4-90]t  We accept th after any "teen" number, or after any digit other than 1..3. For 1..3, we use a negative lookbehind to accept st, nd, or rd only when not preceded by 1. ## Test program #!/usr/bin/bash ok=(✓ ❌) for i do grep -Pq '1.th|(?<!1)(1s|2n|3r)|[4-90]t' <<<"$i"; echo $i${ok[\$?]}
done


### Results

1st ✓
1th ❌
2nd ✓
2th ❌
3rd ✓
3th ❌
4st ❌
4th ✓
11th ✓
11st ❌
12nd ❌
12th ✓
13th ✓
13rd ❌
112nd ❌
112th ✓
21nd ❌
32nd ✓
33rd ✓
21th ❌
21st ✓
11st ❌
111199231923819238198231923213123909808th ✓