| Bytes | Lang | Time | Link |
|---|---|---|---|
| 051 | Scala | 230729T105532Z | 138 Aspe |
| 029 | PCRE Perlcompatible regular expression | 180424T092101Z | Toby Spe |
| 037 | Factor + math.text.english | 220131T012526Z | chunes |
| 007 | Vyxal | 220131T010349Z | lyxal |
| 056 | GeoGebra | 220131T004522Z | Aiden Ch |
| 065 | x86 machine code | 181104T150516Z | Moderate |
| 060 | PHP | 181104T172741Z | Titus |
| 031 | Retina | 180414T175025Z | Endenite |
| 059 | Wolfram Language Mathematica | 180424T072454Z | DELETE_M |
| 068 | Python 2 | 180414T230513Z | Oliver N |
| 122 | Wolfram Language Mathematica | 180420T182527Z | Kelly Lo |
| 063 | Excel | 180420T174021Z | Engineer |
| 051 | Java 8 | 180416T075215Z | Kevin Cr |
| 039 | Ruby | 180417T160143Z | Asone Tu |
| 022 | Jelly | 180415T141529Z | Jonathan |
| 053 | Python | 180415T131154Z | Jonathan |
| 100 | Haskell | 180416T183947Z | Wheat Wi |
| 024 | 05AB1E | 180416T113425Z | Emigna |
| 057 | Perl 5 n | 180416T033348Z | Xcali |
| 070 | Python 2 | 180414T212324Z | Chas Bro |
| 060 | Pyth | 180414T212854Z | hakr14 |
| nan | 180414T172635Z | DanielIn | |
| 054 | Bash + GNU utilities | 180414T165004Z | Digital |
Scala, 51 bytes
Port of @Kevin Cruijssen's Java answer in Scala.
s=>s.matches(".*1.th|(.*[^1])?(1s|2n|3r|[^1-3]t).")
PCRE (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 ✓
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
Vyxal, 7 bytes
₌Ǎ⌊∆o$c
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?
GeoGebra, 56 bytes
s="1nd"
b=Ordinal(FromBase(Take(s,1,Length(s)-2),10))==s
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?
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'
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)
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).
Wolfram Language (Mathematica), 65 59 bytes
SpokenString@p[[#]]~StringTake~{5,-14}&@@ToExpression@#==#&
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.
Python 2, 92 82 74 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.
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")]
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)
Java 8, 54 51 bytes
s->s.matches(".*1.th|(.*[^1])?(1s|2n|3r|[^1-3]t).")
Explanation:
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
Ruby, 42 39 bytes
Lambda:
->s{s*2=~/1..h|[^1](1s|2n|3r|[4-90]t)/}
User input:
p gets*2=~/1..h|[^1](1s|2n|3r|[4-90]t)/
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)/
Same idea as above but instead of duplicating the string, this also matches the start of the string (^).
Jelly, 25 22 bytes
-3 bytes thanks to an observation made in a comment made by on my Python entry.
ḣ-2VDṫ-’Ạ×ɗ/«4ị“snrh”e
A monadic link.
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)
ɗ - last 3 links as a dyad:
’ - 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, 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.
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'.
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"
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
Python 2, 94 82 77 73 70 bytes
lambda s:'tsnrthtddh'[min(4,int(s[-3])*(('0'+s)[-4]!='1'))::5]==s[-2:]
Pyth, 49 60 bytesSBCS
Js<2zK%J100I||qK11qK12qK13q>2z"th".?qz+J@c."dt8¸*£tÎðÎs"2J
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])
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),97 92 78 bytes
s=>("tsnr"[~~((n=(o=s.match(/(\d{1,2})(\D)/))[1])/10%10)-1?n%10:0]||'t')==o[2]
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)
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.
