Creating a program that prints true if three words are entered in dictionary order
Asked Answered
N

3

7

I am trying to create a program that asks the user for three words and prints 'True' if the words are entered in dictionary order. E.G:

Enter first word: chicken

Enter second word: fish

Enter third word: zebra

True

Here is my code so far:

first = (input('Enter first word: '))
second = (input('Enter second word: '))
third = (input('Enter third word: '))
s = ['a','b','c','d','e','f','g','h',
     'i','j','k','l','m','n','o','p',
     'q','r','s','t','u','v','w','x',
     'y','z','A','B','C','D','E','F',
     'G','H','I','J','K','L','M','N',
     'O','P','Q','R','S','T','U','V',
     'W','Z','Y','Z']
if s.find(first[0]) > s.find(second[0]) and s.find(second[0]) >                                  s.find(third[0]):
    print(True)
Numismatist answered 17/10, 2015 at 6:6 Comment(4)
Do you mean alphabetical order?Smelser
Tip: string module has a variable called ascii_letters.Heptad
This should be moved to Code Review.Autodidact
@Autodidact - This should not be moved to CR, because it's not working code.Amalbergas
V
8

If you work on a list of arbitrary length, I believe using sorted() as other answers indicate is good for small lists (with small strings) , when it comes to larger lists and larger strings and cases (and cases where the list can be randomly ordered), a faster way would be to use all() built-in function and a generator expression (This should be faster than the sorted() approach). Example -

#Assuming list is called lst
print(all(lst[i].lower() < lst[i+1].lower() for i in range(len(lst)-1)))

Please note, above would end up calling str.lower() on every string (except for first and last) twice. Unless your strings are very large, this should be fine.

If your strings are really very large compared to the length of the list, you can create another temporary list before doing the all() that stores all the strings in lowercase. And then run same logic on that list.

You can create your list (by taking inputs from the user) using a list comprehension, Example -

lst = [input("Enter word {}:".format(i)) for i in range(3)] #Change 3 to the number of elements you want to take input from user.

Timing results of the above method vs sorted() (modified code of sorted() to work case-insensitively) -

In [5]: lst = ['{:0>7}'.format(i) for i in range(1000000)]

In [6]: lst == sorted(lst,key=str.lower)
Out[6]: True

In [7]: %timeit lst == sorted(lst,key=str.lower)
1 loops, best of 3: 204 ms per loop

In [8]: %timeit all(lst[i].lower() < lst[i+1].lower() for i in range(len(lst)-1))
1 loops, best of 3: 439 ms per loop

In [11]: lst = ['{:0>7}'.format(random.randint(1,10000)) for i in range(1000000)]

In [12]: %timeit lst == sorted(lst,key=str.lower)
1 loops, best of 3: 1.08 s per loop

In [13]: %timeit all(lst[i].lower() < lst[i+1].lower() for i in range(len(lst)-1))
The slowest run took 6.20 times longer than the fastest. This could mean that an intermediate result is being cached
100000 loops, best of 3: 2.89 µs per loop

Result -

For cases that should return True (that is already sorted lists) the using sorted() is quite faster than all() , since sorted() works well for mostly-sorted lists.

For cases that are random, all() works better than sorted() because of the short-circuiting nature of the all() (it would short-circuit when it sees the first False ).

Also, there is the fact that sorted() would create a temporary (sorted list) in memory (for comparison), whereas all() would not require that (and this fact does attribute to the timings we see above).


Earlier answer that directly (and only applies to this question) you can simply directly compare the strings as such, you do not need another string/list for alphabets. Example -

first = (input('Enter first word: '))
second = (input('Enter second word: '))
third = (input('Enter third word: '))
if first <= second <= third:
    print(True)

Or if you want to compare only the first characters (though I highly doubt that) -

if first[0] <= second[0] <= third[0]:
    print(True)

To compare the strings case-insensitively, you can convert all the string to lowercase, before comparison. Example -

if first.lower() <= second.lower() <= third.lower():
    print(True)

Or the simpler -

print(first.lower() <= second.lower() <= third.lower())
Villagomez answered 17/10, 2015 at 6:9 Comment(18)
The first example is perfect for what I am looking for, but is there any way to make it so that if one of those words starts with a capital letter it won't affect the impact of it being true or false?Numismatist
Added an example for that, convert all strings to same case, and compare. That way the case would not matter.Villagomez
It already looks bloated with just three strings. You didn't even put them into a list. This answer is full of poor programming habits, which I will most certainly downvote.Amalbergas
@Amalbergas If its still worthy of a downvote, you should also indicate what poor programming habit is in the answer.Villagomez
Hard-coding multiple numbered variables, e.g. first, second, etc., is a poor habit. Not putting your variables into a list or tuple or other sequence - even if they're hard-coded - is another. This leads to another bad habit of chaining a comparison for each variable when you just want to check whether they're sorted, for which a function already exists. The problem with this is that the people who think this is good code will eventually get bitten by these habits, and you and I both know that their messes will turn into SO questions.Amalbergas
That is all OP's code , I have indicated how to create a list from inputs in the second part of the answer, and I am pretty sure using all for this check if better than sorted() (faster).Villagomez
If you saw someone's Tkinter app with multiple mainloop() calls, would you try to make the code work with those multiple calls? Or would you tell them not to make multiple mainloop calls and show them how to do it right?Amalbergas
Like I said there is a second part to the answer, why don't you read it up?Villagomez
Did you measure whether the all method is faster than sorted (which has good performance on mostly-sorted sequences) before you optimized it at the cost of readability?Amalbergas
I'm sorry, but nothing in the second part could possibly justify an upvote to the first part. I don't understand why you're defending that terrible code - your usual standards are much higher.Amalbergas
@AnandSKumar what this if first.lower() <= second.lower() <= third.lower() line of code does?Jarodjarosite
@AvinashRaj It checks if first is smaller than second and second is smaller than third case-insensitively.Villagomez
alphabetical (lexicographical)Villagomez
"there is the fact that for sorted() to work for case-insensitive, it would require the creation of another list" - nope. See my answer.Amalbergas
@Amalbergas print does not have a key argument.Villagomez
No, but sorted does, which is why it's the right approach here.Amalbergas
@Amalbergas Ok, I meant in your example, you have put key=str.lower for print .Villagomez
Ah, I see - I put the parenthesis in the wrong place; sorry about the typo. Still didn't have to create a whole new list for a case-insensitive sort.Amalbergas
C
6

Yes, lists do not have the find method. Though you don't even have to use lists. The <= (as well as >=) operator compare sequences lexicographically. In addition, Python supports chained comparisons. Here's how I'd write it:

first = input('Enter first word: ')
second = input('Enter second word: ')
third = input('Enter third word: ')
print(first <= second <= third)

If there're more than 3 words, you'd gather them into a list, sort it and compare it against the source list. Example:

words = input('Enter words (separated by whitespace): ').split()
print(sorted(words) == words) # True if sorted() didn't re-order anything

Both of these approaches will work reasonably well for small number of words. If the number of words is big, you should consider using a short-circuiting solution using the built-in all function and a generator expression:

prev_it, cur_it = iter(words), iter(words)
# Consume first element
next(cur_it)
# Pairwise iteration
print(all(prev <= cur for prev, cur in zip(prev_it, cur_it)))

which is an efficient generalization of the first solution.


If you want to perform case-insensitive comparison, use str.lower (or str.casefold, in Python 3.3+).

Example for the first code snippet:

print(first.lower() <= second.lower() <= third.lower())

Example for the list-based approaches:

words = [s.lower() for s in input('Enter words (separated by whitespace): ').split()]
Corell answered 17/10, 2015 at 6:9 Comment(0)
A
3

Store the words in a list and then check it with sorted(). You can make it ignore case by specifying a key that looks at the lowercase version of each word for comparison:

words = input("Enter three words, separated by spaces: ").split()
print(words == sorted(words, key=str.lower))
Amalbergas answered 17/10, 2015 at 6:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.