3

I would like to find dynamically the correct ordinal number root for instance:

111 = 111st
112 = 112nd 
113 = 113rd ...

I tried other solutions but I can't find a good one.

This is my code:

for number in range(1, 114):
    print(number)
    ex1 = 11
    ex2 = 12
    ex3 = 13
    if number == ex1:
        print("is the " + str(number) + "th number.")
    elif number % 10 == 1 or not ex1:
        print("is the " + str(number) + "st number.")
    elif number == ex2:
        print("is the " + str(number) + "nd number.")
    elif number % 10 == 2 or not ex2:
        print("is the " + str(number) + "nd number.")
    elif number == ex3:
        print("is the " + str(number) + "rd number.")
    elif number % 10 == 3 or not ex3:
        print("is the " + str(number) + "rd number")
    else:
        print("is the " + str(number) + "th number.")
sebix
  • 2,943
  • 2
  • 28
  • 43
Revoked
  • 33
  • 4
  • I dont really understand what you are trying to accomplish. Do you just want "1 is the 1st number" "2 is the 2nd number" ... ... ... "114 is the 114th number"? –  Dec 30 '20 at 14:36
  • It seems to be running fine - it did output the expected formatted numbers. So what's your question? – Daniel Hao Dec 30 '20 at 14:36
  • 3
    This isn't a problem with for loops; it's a problem with the logic inside the loop. Please read [how to debug small programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) and try to diagnose where the logic is wrong. In particular, check which numbers give the wrong result, and try to trace through your logic with those values. – Karl Knechtel Dec 30 '20 at 14:37
  • So basically you're trying to print cardinal numbers in a given range and are looking for a shorter way to do it? – Warisul Dec 30 '20 at 14:37
  • `or not ext1` and similar is obviously not what the OP intends, but due to the order of conditions, shouldn't actually break it. – SpoonMeiser Dec 30 '20 at 14:41
  • Yes, I am trying out a python exercise. I think my if statements are off that's why as soon as I hit 111, it just prints out "111 is the 111st number." Which is not what I want, same goes for 112 and 113 – Revoked Dec 30 '20 at 14:41
  • 1
    Well, that's because the condition `111 == 11` isn't `True` :) You perhaps need to find a way to test is a number ends in the exception, rather than just being equal to it – SpoonMeiser Dec 30 '20 at 14:50
  • You should edit this question to make it more explicit that the problem is that the numbers 111, 112 and 113 aren't displayed correctly, since I think a lot of answers I simply addressing other points about the code. – SpoonMeiser Dec 30 '20 at 15:20

4 Answers4

3

Note that 11, 12 and 13 have th suffix.
Also note you can change the end of the line in print function (default \n):

print('text', end=' ')
print('another text')

Then, I suggest you to use formatted string using f"{data} constant text" or "{} constant text".format(data).

Here is my solution to your problem:

def getSuffix(n):
    if n < 0: raise Exception("Ordinal negative numbers are not allowed")
    if n % 100 in [11, 12, 13]: return 'th'
    if n % 10 == 1: return 'st'
    if n % 10 == 2: return 'nd'
    if n % 10 == 3: return 'rd'
    return 'th'


for number in range(1, 114):
    print(f"{number} is the {number}{getSuffix(number)} number")

I hope I was helpful.

M3601
  • 82
  • 5
  • Did you modify the source? – M3601 Dec 30 '20 at 15:09
  • Oh, your code is OK (+1), I have to reset my REPL. – MarianD Dec 30 '20 at 15:13
  • 3
    I think the problem is that, for example, `111` gets the suffix `st` instead of `th`, which I think is still a problem with this code. I think you should change the second condition to `if n % 100 in [11, 12, 13]:` to fix this. – SpoonMeiser Dec 30 '20 at 15:13
  • @SpoonMeiser, the answerer is probably not a native English speaker, so they don't know nuances. – MarianD Dec 30 '20 at 15:17
  • Yes, I'm not a native English speaker – M3601 Dec 30 '20 at 15:22
  • @MarianD but that nuance is the problem that the OP was actually trying to solve :) - it really wasn't clear from the question though. I've upvoted this answer though because I think the code is really nice and clear. – SpoonMeiser Dec 31 '20 at 10:18
2

This is a pretty good solution:

ordinal = lambda n: "%d%s" % (n, "tsnrhtdd"[(n // 10 % 10 != 1) * (n % 10 < 4) * n % 10::4])


for number in range(1, 114):
    print(f'the {ordinal(number)} number. :) ')

EDIT For Human Beings

NOTE: Variables name aren't meant to be used in a production environment, I tried to make it more explicit what each step on the lambda function does!

def get_ordinal(n):

    hacking_string = "tsnrhtdd"                                # 1)
    is_num_divisible_by_ten = (n // 10 % 10 != 1)              # 2)         
    is_num_reminder_0_3= (n % 10 < 4)                          # 3)
    are_both_false_or_both_true= is_num_divisible_by_ten * is_num_between_0_3  # 4)
    get_index = are_both_false_or_both_true* n % 10  # 5)
    
    return f'{number}{hacking_string[get_index::4]}'  #6)  ---> starts at t | s | n | r
    
for number in range(1, 114):
    print(f'the {get_ordinal(number)} number. :) ')

Considerations

The solution found is very Hacky and smart and I probably would never come up my self, is using some clever math tricks to find the off sets of the number. As requested I however simplified the function and added some explanation to it.

  • Step 1. This string is better seen it as this "tsnr" "htdd" | On the left side you heve the "root" of the string, on the right the end. (more explanation below)

  • Step 2. is_num_divisible_by_ten --> using a floor division the result is True or False.

  • Step 3. is_num_reminder_0_3 If checking if the reminder of N and 10 is between 0 & 3, returns a True / False Flag.

  • Step 4. are_both_false_or_both_true is multiplying 2 bool value, in Python True is a 1 and False is a 0, so is like do --> 1 * 0. The Variable is True only if both values are True or both are False, otherwise is always False.

  • Step 5. get_index - > Returns either 0 or 1 or 2 or 3.

  • Step 6. Here the hacky part, with the received index from get_index, is laverage the hacking_string variable with indexing-and-slicing:

The get_index value is always one of these: "tsnr" and the steps taken (4) any of these "rhtdd" hence the possible combination are:

get_index = 0 = "th"  
get_index = 1 = "st"
get_index = 2 = "nd"
get_index = 3 = "rd"

Finally

The exact mathematics that goes behind it may be better asked on math.stackexchange or if someone knows it would be good to either add a comment or edit my answer!

References (It wasn't my solution)

Guides

SpoonMeiser
  • 19,918
  • 8
  • 50
  • 68
Federico Baù
  • 6,013
  • 5
  • 30
  • 38
  • 2
    If this were code golf, maybe. I don't think this is at all easy to read – SpoonMeiser Dec 30 '20 at 14:39
  • 1
    Indeed `cool.` But it's questionable to have alias function name `ordinal` for the `lambda` function (in practice)? Except that point, Thumb Up! – Daniel Hao Dec 30 '20 at 14:41
  • 1
    Since the questioner is a beginner, it'd be nice if you explained what you're doing in your code. Cheers for the neat (in a certain way) solution! – Warisul Dec 30 '20 at 14:43
  • Missing space in `the 1stnumber. :)`, ... etc. output. – MarianD Dec 30 '20 at 15:24
  • @MarianD added the space for You , good catch | Daniel Hao & Waris I did add an explanation now the best I could, is honestly one of those solution find using some good math that seems Magic but it always work, Agree that it should be more clear! When I added the answer I just found it – Federico Baù Dec 30 '20 at 15:35
  • @FedericoBaù, I appreciated this solution and voted your answer up yet before you corrected and extended it — for its curiosity and for your honesty, too. ;-) – MarianD Dec 30 '20 at 16:24
2

So, the problem is that 111 gets displayed as 111st instead of 111th.

You have 11 as ex1, I assume short for "exception 1", but your condition:

if number == ex1:

Clearly doesn't match 111.

Instead you could do:

if number % 100 == ex1:

Which will be true for 11, 111, 211 etc.

On a side note:

elif number % 10 == 1 or not ex1:

Clearly isn't what you intended. This is interpreted as:

elif (number % 10 == 1) or (not ex1):

not ex1 does not depend on number and will always evaluate the same way (False). But since you're already checking ex1 separately, it would be redundant to do it correctly here.

If you wanted to correct that, so that you don't need to check ex1 twice, you'd do this:

if number % 10 == 1 and number % 100 != 11:

I think in this case using != is clearer than not and I don't think there is any benefit from assigning a variable to 11.

SpoonMeiser
  • 19,918
  • 8
  • 50
  • 68
0

You can do it like that:

for number in range(1, 114):
    printedString = str(number)+' is the '+str(number)
    if str(number) == '1' or (str(number)[-1] == '1' and str(number)[-2] != '1'):
        printedString += 'st'
    elif str(number) == '2' or (str(number)[-1] == '2' and str(number)[-2] != '1'):
        printedString += 'nd'
    elif str(number) == '3' or (str(number)[-1] == '3' and str(number)[-2] != '1'):
        printedString += 'rd'
    else:
        printedString += 'th'
    print(printedString+' number.')
simonarys
  • 55
  • 1
  • 6