# 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, 2018 at 17:00
• They are normal rules. I changed nothing. But thanks for the input , I added a link Apr 14, 2018 at 17:02
• @Jonathan Allan Ordinal numbers start from 1st, negative ordinals do not exist - english.stackexchange.com/questions/309713/… Apr 14, 2018 at 23:18
• @JonathanAllan OP says "Input is going to be valid ordinal pattern." which means no negatives Apr 15, 2018 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. Apr 20, 2018 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 Apr 17, 2018 at 16:14
• @AsoneTuhid nice golf - thanks! Apr 17, 2018 at 16:41
• @AsoneTuhid - also saved three on my Jelly answer, so double the thanks! Apr 17, 2018 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. Apr 14, 2018 at 16:58 • @MuhammadSalman That's because it's a test suite. Take a look at the exit codes for 1st and 1th. Apr 14, 2018 at 17:09 • egrep is capable of addition and primality testing (in unary), so I think you can make this an egrep answer. Apr 14, 2018 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 ? Apr 14, 2018 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!. Apr 14, 2018 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, 2018 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, 2018 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!

• 90 bytes: Try it online! Apr 21, 2018 at 12:06

# 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


# GeoGebra, 56 bytes

s="1nd"
b=Ordinal(FromBase(Take(s,1,Length(s)-2),10))==s


Try It On GeoGebra!

Input is s, Output is b. b is true if s is a valid ordinal, false otherwise.

I just realized that GeoGebra has string support, so I had to try it.

### Explanation:

Ordinal(FromBase(Take(s,1,Length(s)-2),10))==s
Take(s,1,Length(s)-2)                s without the last two characters
FromBase(                     ,10)            Convert the string to a number
Ordinal(                                  )           Convert the number to ordinal form
==s        Is it equal to s?


# Vyxal, 7 bytes

₌Ǎ⌊∆o$c  Try it Online! Because we have a built-in for that. ## Explained ₌Ǎ⌊∆o$c
₌Ǎ⌊     # Push the letters and numbers of the input
∆o      # Push the corresponding ordinal string
$c # are the letters of the string contained in the ordinal string?  # Factor + math.text.english, 37 bytes [ 2 cut* swap dec> ordinal-suffix = ]  ## Explanation  ! "121th" 2 cut* ! "121" "th" swap ! "th" "121" dec> ! "th" 121 ordinal-suffix ! "th" "st" = ! f  # 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
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'
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)

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 ✓