28

I'm wondering if there is a quick and easy way to output ordinals given a number in python.

For example, given the number 1, I'd like to output "1st", the number 2, "2nd", et cetera, et cetera.

This is for working with dates in a breadcrumb trail

Home >  Venues >  Bar Academy >  2009 >  April >  01 

is what is currently shown

I'd like to have something along the lines of

Home >  Venues >  Bar Academy >  2009 >  April >  1st
Regexident
  • 29,441
  • 10
  • 93
  • 100
Mez
  • 24,430
  • 14
  • 71
  • 93

15 Answers15

46

Or shorten David's answer with:

if 4 <= day <= 20 or 24 <= day <= 30:
    suffix = "th"
else:
    suffix = ["st", "nd", "rd"][day % 10 - 1]
Mez
  • 24,430
  • 14
  • 71
  • 93
Abizern
  • 146,289
  • 39
  • 203
  • 257
  • 8
    I did not know about the "if x <= y <= z" syntax, couldn't believe it would work and tried it. Looks like I should re-read the Python doc *again* :) –  Apr 11 '09 at 00:55
  • That way is kind of hard to generalize though - it gets clunky if you want to use it for arbitrary numbers. – David Z Apr 11 '09 at 01:02
  • 5
    True, but the OP was asking about ordinal dates. Since YAGNI, it would be better to choose the shorter, more elegant solution that meets his needs, even if it's not generalizable. If someone wants a generalized solution, your's is good, though I'd probably use a list rather than the elseifs. – Chris Upchurch Apr 11 '09 at 01:44
29

Here's a more general solution:

def ordinal(n):
    if 10 <= n % 100 < 20:
        return str(n) + 'th'
    else:
       return  str(n) + {1 : 'st', 2 : 'nd', 3 : 'rd'}.get(n % 10, "th")
CTT
  • 16,901
  • 6
  • 41
  • 37
13

Not sure if it existed 5 years ago when you asked this question, but the inflect package has a function to do what you're looking for:

>>> import inflect
>>> p = inflect.engine()
>>> for i in range(1,32):
...     print p.ordinal(i)
...
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
alukach
  • 5,921
  • 3
  • 39
  • 40
5

These days I'd use Arrow http://arrow.readthedocs.io/en/latest/ (which definately wasn't around in '09)

>>> import arrow
>>> from datetime import datetime
>>> arrow.get(datetime.utcnow()).format('Do')
'27th'
Carl
  • 853
  • 9
  • 23
2

A more general and shorter solution (as a function):

def get_ordinal(num)
    ldig = num % 10
    l2dig = (num // 10) % 10

    if (l2dig == 1) or (ldig > 3):
        return '%d%s' % (num, 'th')
    else:
        return '%d%s' % (num, {1: 'st', 2: 'nd', 3: 'rd'}.get(ldig))

I just combined David's solutions and libraries (as deegeedubs did). You can even replace the variables (ldig, l2dig) for the real math (since l2dig is used only once), then you get four lines of code.

2

Here it is using dictionaries as either a function or as a lambda...

If you look at the dictionaries backwards you can read it as...

Everything ends in 'th'

...unless it ends in 1, 2, or 3 then it ends in 'st', 'nd', or 'rd'

...unless it ends in 11, 12, or 13 then it ends in 'th, 'th', or 'th'

# as a function
def ordinal(num):
    return '%d%s' % (num, { 11: 'th', 12: 'th', 13: 'th' }.get(num % 100, { 1: 'st',2: 'nd',3: 'rd',}.get(num % 10, 'th')))

# as a lambda
ordinal = lambda num : '%d%s' % (num, { 11: 'th', 12: 'th', 13: 'th' }.get(num % 100, { 1: 'st',2: 'nd',3: 'rd',}.get(num % 10, 'th')))
eric.frederich
  • 1,598
  • 4
  • 17
  • 30
2

I made a function that seems to work in this case. Just pass in a date object, and it will use the day to figure out the suffix. Hope it helps

from datetime import date
def get_day_ordinal(d):

    sDay = '%dth'
    if d.day <= 10 or d.day >= 21:
        sDay = '%dst' if d.day % 10 == 1 else sDay
        sDay = '%dnd' if d.day % 10 == 2 else sDay
        sDay = '%drd' if d.day % 10 == 3 else sDay

    return sDay % d.day

d = date.today()
print get_day_ordinal(d)
Eric
  • 229
  • 2
  • 10
1
def ordinal(n):
    return ["th", "st", "nd", "rd"][n%10 if n%10<4 and not (10<n%100<14) else 0]
SwiftsNamesake
  • 1,540
  • 2
  • 11
  • 25
1

Except for 1st, 2nd, and 3rd, I think they all just add th... 4th, 5th, 6th, 11th, 21st ... oh, oops ;-)

I think this might work:

def ordinal(num):
     ldig = num % 10
     l2dig = (num // 10) % 10
     if l2dig == 1:
         suffix = 'th'
     elif ldig == 1:
         suffix = 'st'
     elif ldig == 2:
         suffix = 'nd'
     elif ldig == 3:
         suffix = 'rd'
     else: 
         suffix = 'th'
     return '%d%s' % (num, suffix)
David Z
  • 128,184
  • 27
  • 255
  • 279
  • The 10's digit is found incorrectly; 3rd line should be: `l2dig = (num // 10) % 10` (Note that this still doesn't work for negative inputs) – not-just-yeti Jan 21 '12 at 15:43
1

Fixed for negative-inputs, based on eric.frederich's nice sol'n (just added abs when using %):

def ordinal(num):
    return '%d%s' % (num, { 11: 'th', 12: 'th', 13: 'th'}.get(abs(num) % 100, { 1: 'st',2: 'nd',3: 'rd',}.get(abs(num) % 10, 'th')))
not-just-yeti
  • 17,673
  • 1
  • 18
  • 15
0

I had to convert a script over from javascript where I had a useful fn that replicated phps date obj. Very similar

def ord(n):
    return str(n)+("th" if 4<=n%100<=20 else {1:"st",2:"nd",3:"rd"}.get(n%10, "th"))

this tied in with my date styler:

def dtStylish(dt,f):
    return dt.strftime(f).replace("{th}", ord(dt.day))

ps -I got here from another thread which was reported as a duplicate but it wasn't entirely since that thread also addressed the date issue

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
Frosty Snowman
  • 435
  • 5
  • 5
0

I wanted to use ordinals for a project of mine and after a few prototypes I think this method although not small will work for any positive integer, yes any integer.

It works by determiniting if the number is above or below 20, if the number is below 20 it will turn the int 1 into the string 1st , 2 , 2nd; 3, 3rd; and the rest will have "st" added to it.

For numbers over 20 it will take the last and second to last digits, which I have called the tens and unit respectively and test them to see what to add to the number.

This is in python by the way, so I'm not sure if other languages will be able to find the last or second to last digit on a string if they do it should translate pretty easily.

def o(numb):
    if numb < 20: #determining suffix for < 20
        if numb == 1: 
            suffix = 'st'
        elif numb == 2:
            suffix = 'nd'
        elif numb == 3:
            suffix = 'rd'
        else:
            suffix = 'th'  
    else:   #determining suffix for > 20
        tens = str(numb)
        tens = tens[-2]
        unit = str(numb)
        unit = unit[-1]
        if tens == "1":
           suffix = "th"
        else:
            if unit == "1": 
                suffix = 'st'
            elif unit == "2":
                suffix = 'nd'
            elif unit == "3":
                suffix = 'rd'
            else:
                suffix = 'th'
    return str(numb)+ suffix

I called the function "o" for ease of use and can be called by importing the file name which I called "ordinal" by import ordinal then ordinal.o(number).

Let me know what you think :D

P.S. I've posted this answer on another ordinals question but realised this one is more applicable considering it's python.

Houngan
  • 69
  • 1
  • 2
0

Here is an even shorter general solution:

def foo(n):
    return str(n) + {1: 'st', 2: 'nd', 3: 'rd'}.get(4 if 10 <= n % 100 < 20 else n % 10, "th")

Although the other solutions above are probably easier to understand at first glance, this works just as well while using a bit less code.

0

Here's a function I wrote as part of a calendar type of program I wrote (I'm not including the whole program). It adds on the correct ordinal for any number greater than 0. I included a loop to demo the output.

def ordinals(num):
    # st, nums ending in '1' except '11'
    if num[-1] == '1' and num[-2:] != '11':
        return num + 'st'
    # nd, nums ending in '2' except '12'
    elif num[-1] == '2' and num[-2:] != '12':
        return num + 'nd'
    # rd, nums ending in '3' except '13'
    elif num[-1] == '3' and num[-2:] != '13':
        return num + 'rd'
    # th, all other nums
    else:
        return num + 'th'

data = ''

# print the first 366 ordinals (for leap year)
for i in range(1, 367):
    data += ordinals(str(i)) + '\n'

# print results to file
with open('ordinals.txt', 'w') as wf:
   wf.write(data)
Michael Swartz
  • 858
  • 2
  • 15
  • 27
0

A transparent solution without any gymnastics:

_DATE_DAY_SUFFIXES = {
    1: 'st',
    2: 'nd',
    3: 'rd',
}

def day_suffix(d):

    if 10 <= d <= 19:
        return 'th'

    d = d % 10

    try:
        return _DATE_DAY_SUFFIXES[d]
    except KeyError:
        return 'th'
Dustin Oprea
  • 9,673
  • 13
  • 65
  • 105