My disgusting list comprehension in the return statement is quite a headful.

"""Hopefully this function will save you the trip to oocalc/excel.


def rangeth(start, stop=None, skip=1):
    """rangeth([start,] stop[, skip])

returns a list of strings as places in a list (1st, 2nd, etc)

>>> rangeth(4)
['0th', '1st', '2nd', '3rd']


    if stop is None:
        stop, start = start, 0

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

    return ["{}{}".format(i, places.get(i[-1], 'th')) \
            if i[-2:] not in ['11', '12', '13'] else "{}{}".format(i, 'th') \
            for i in map(str, range(start, stop, skip))]

Also, can someone explain to me how range accepts it's parameters? I have my ugly little boilerplate here that I wish didn't exist. I can't find the source for range, as I gave up after thinking it's probably some header file in include.

  • \$\begingroup\$ Range - docs.python.org/library/functions.html#range. Which version of Python are you using? I'm a novice at python, but one thing I know is that yield (stackoverflow.com/questions/231767/…) would allow using loops which would most likely make this code much more readable (but maybe not as efficient, I don't know). \$\endgroup\$
    – Pat
    Commented Feb 24, 2012 at 17:55
  • \$\begingroup\$ This won't work version <= 2.6 due to the .format() function. Also, your link to the doc, though appreciated, isn't what I was after. If you look at the calltip in IDLE for range, there are no default values given. My function posts rangeth(start, stop=None..., which I don't like. \$\endgroup\$
    – yurisich
    Commented Feb 24, 2012 at 18:21
  • \$\begingroup\$ Aha! I found what I was looking for. \$\endgroup\$
    – yurisich
    Commented Feb 24, 2012 at 18:29
  • \$\begingroup\$ I think your new version fails for 121. \$\endgroup\$ Commented Feb 25, 2012 at 3:36
  • 1
    \$\begingroup\$ There is a module: inflect pypi.python.org/pypi/inflect/0.2.1 It has an ordinal-function which may be an alternate solution! ;-) \$\endgroup\$ Commented Mar 5, 2012 at 17:20

2 Answers 2


Firstly, I'd suggest not duplicating the overloading logic of range. Instead:

def rangeth(*args):
    print range(*args)

Just take your arguments and pass them onto to range.

Secondly, I'd write a function that takes a single number and produces the "th"ified version.

def as_th(number):
    number = str(number)
    if number[-2:] in ['11','12','13]:
         return "{}{}".format(number, 'th')
         return "{}{}".format(number, places.get(number[-1], 'th'))

I think that makes it easier to follow.

Then have you rangeth function be:

def rangeth(*args):
    return map(as_th, range(*args))
  • \$\begingroup\$ I didn't like returning the map object. I just used return [ nth(x) for x in range(*args) ]. But, +1 for producing the stand-alone nth(n) function, which is nice. \$\endgroup\$
    – yurisich
    Commented Feb 25, 2012 at 1:09
  • \$\begingroup\$ Follow up: didn't realize that Python 3k returns map objects, which is why I wasn't getting a list back. \$\endgroup\$
    – yurisich
    Commented Feb 25, 2012 at 1:20

Adding my 2cents as a two-liner:

mark=lambda p:'{0}{1}'.format(p,('th','st','nd','rd')[p] if p%10<4 and p/10 != 1 else 'th')
map(mark, xrange(30))

delievers: ['0th', '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']

  • \$\begingroup\$ Doesn't this miss the logic for numbers like "21st" \$\endgroup\$ Commented Feb 24, 2012 at 20:18
  • 1
    \$\begingroup\$ This is obviously a function that could be reused and would be relevant in many situations, so turning it into a lambda is unnecessary and kills readability. \$\endgroup\$
    – Pat
    Commented Feb 25, 2012 at 1:06
  • \$\begingroup\$ A couple things...p can quickly climb out of the range of ('th', 'st', 'nd', 'rd') tuple. I fixed it, and will post it in my updated solution (I liked the fact that you never converted to str. So, net +1). \$\endgroup\$
    – yurisich
    Commented Feb 25, 2012 at 2:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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