Home > Net >  Refactoring ordinal number printing algorithm
Refactoring ordinal number printing algorithm

Time:12-31

I'm working through Python Crash Course (end of Chapter 5). There is an example question to print the correct ending for the ordinal numbers 1 - 9, 1"st", 2"nd" etc. I expanded the example so it should work for any number, below.

How could this be written more concisely? What concepts could I look up? I'm an intermediate R user but quite new to Python. Thank you

nums = list(range(1, 50))

for i in nums:
    if i % 10 == 1 and i % 100 != 11:
        end = "st"
    elif i % 10 == 2 and i % 100 != 12:
        end = "nd"
    elif i % 10 == 3 and i % 100 != 13:
        end = "rd"
    else:
        end = "th"
    print(f"{i}{end}")

CodePudding user response:

As mentioned in the comments, a dict can be used for lookup logic like this. The complication is when the number's last two digits are in the "teens". An example dict that would be useful in your case is:

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

And you could use it in the body of your for loop as such:

if 10 < i % 100 < 20:
    end = 'th'
else:
    end = endings.get(i % 10, 'th')  # The second arg to the `get` method returns a default value

Going another step further, you can make this even more concise by combining the lookup logic and taking advantage of logic short-circuiting:

end = endings.get(not 10 < i % 100 < 20 and i % 10, 'th')

But I really don't recommend that! It's awfully yucky and confusing to the reader.

To be honest, your original example is the most readable among these options. You have to weigh the value of conciseness vs readability before choosing what to do.

CodePudding user response:

Alternative without as many if/else conditions

values_10 = [1, 2, 3]                                          # Numbers to equal mod 10
exceptions_100 = [11, 12, 13]                                  # Numbers not to equal mod 100
endings = ['st', 'nd', 'rd', 'th']                             # Set of endings
for i in range(1, 50):
    i_mod_10, i_mod_100 = i % 10, i % 100                      # Compute i mod 10 and i mod 100
    try:
        idx = values_10.index(i_mod_10)                        # Get index of match for 1, 2, or 3 (if exists)
        # Since no exception ending was 1, 2, or 3
        # end is based on whether i_mod_100 matches it's corresponding value
        end = endings[-1] if i_mod_100 == exceptions_100[idx] else endings[idx]  
    except ValueError:
        end = endings[-1]                                       # i_mod_10 was not 1, 2, or 3
    print(f"{i}{end}")
  • Related