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)
vaultah
  • 44,105
  • 12
  • 114
  • 143
Joe Dingle
  • 123
  • 1
  • 4
  • 12

3 Answers3

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())
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
  • 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? – Joe Dingle Oct 17 '15 at 06:14
  • Added an example for that, convert all strings to same case, and compare. That way the case would not matter. – Anand S Kumar Oct 17 '15 at 06:19
  • 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. – TigerhawkT3 Oct 17 '15 at 11:00
  • @TigerhawkT3 If its still worthy of a downvote, you should also indicate what poor programming habit is in the answer. – Anand S Kumar Oct 17 '15 at 11:01
  • 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. – TigerhawkT3 Oct 17 '15 at 11:12
  • 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). – Anand S Kumar Oct 17 '15 at 11:15
  • 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? – TigerhawkT3 Oct 17 '15 at 11:17
  • Like I said there is a second part to the answer, why don't you read it up? – Anand S Kumar Oct 17 '15 at 11:18
  • 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? – TigerhawkT3 Oct 17 '15 at 11:23
  • 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. – TigerhawkT3 Oct 17 '15 at 11:25
  • @AnandSKumar what this `if first.lower() <= second.lower() <= third.lower()` line of code does? – Avinash Raj Oct 17 '15 at 14:03
  • @AvinashRaj It checks if `first` is smaller than `second` and `second` is smaller than `third` case-insensitively. – Anand S Kumar Oct 17 '15 at 14:05
  • alphabetical (lexicographical) – Anand S Kumar Oct 17 '15 at 14:07
  • "there is the fact that for sorted() to work for case-insensitive, it would require the creation of another list" - nope. See my answer. – TigerhawkT3 Oct 17 '15 at 20:33
  • @TigerhawkT3 `print` does not have a `key` argument. – Anand S Kumar Oct 18 '15 at 04:59
  • No, but `sorted` does, which is why it's the right approach here. – TigerhawkT3 Oct 18 '15 at 05:00
  • @TigerhawkT3 Ok, I meant in your example, you have put `key=str.lower` for `print` . – Anand S Kumar Oct 18 '15 at 05:10
  • 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. – TigerhawkT3 Oct 18 '15 at 05:14
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()]
vaultah
  • 44,105
  • 12
  • 114
  • 143
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))
jesterjunk
  • 2,342
  • 22
  • 18
TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97