15
\$\begingroup\$

I have written a Python program to take in two strings and print the larger of the two strings.

Here is my code -

string1 = input("Enter first string: ")
string2 = input("Enter second string: ")
count1 = 0
count2 = 0
for i in string1:
      count1 = count1 + 1

for j in string2:
      count2 = count2 + 1

if (count1 < count2):
      print ("Larger string is:")
      print (string2)

elif (count1 == count2):
      print ("Both strings are equal.")
else:
      print ("Larger string is:")
      print (string1)

Here are some example outputs -

Enter first string: everything
Enter second string: nothing
Larger string is:
everything

Enter first string: cat
Enter second string: apple
Larger string is:
apple

I feel that my code is unnecessarily long. Therefore, I would like to know whether I could make this program shorter and more efficient.

Any help would be highly appreciated.

\$\endgroup\$
  • 1
    \$\begingroup\$ So "larger" is length, not lexically sorted last? i.e. "aa" is larger than "z". Normally we'd use the word "longer" to make it 100% clear we're talking just about length, not some other comparison predicate like alphabetical order, i.e. first mismatching character. \$\endgroup\$ – Peter Cordes May 24 at 17:14
35
\$\begingroup\$

Python strings supports Python built-in len function. You don't need to iterate through them manually, as for lists/dicts/sets etc (it is not Pythonic):

def compare_strings_len(s1, s2):
    if len(s1) > len(s2):
        print('String 1 is longer: ', s1)
    elif len(s1) < len(s2):
        print('String 2 is longer: ', s2)
    else:
        print('Strings length are equal!')
\$\endgroup\$
  • \$\begingroup\$ Upvoted! Thanks, I will keep in mind to use inbuilt functions as they make programs easier to write. \$\endgroup\$ – Justin May 22 at 14:06
  • 2
    \$\begingroup\$ ...and easier to read! \$\endgroup\$ – Weirdo May 27 at 14:29
35
\$\begingroup\$

Here's how I would get the longer string:

max(string_1, string_2, key=len)  # Returns the longer string

The key keyword argument is a pattern you'll see frequently in python. It accepts a function as an argument (in our case len).

If you wanted to find the longest of multiple strings, you could do that too:

max('a', 'bc', 'def', 'ghi', 'jklm', key=len)  # => 'jklm'

Warning:

This solution is not a great fit if you need to know when two strings are of equal length. If that's a requirement of yours, you'd be better off using a solution from one of the other answers.

I won't bother updating this approach to handle that requirement: that would feel like working against the language.

\$\endgroup\$
  • 12
    \$\begingroup\$ Bear in mind that max will not work as intended if both string are the same len \$\endgroup\$ – Thomas Ayoub May 23 at 8:55
  • 6
    \$\begingroup\$ Yeah, for this answer to be correct and true to the functionality, there has to be an equality check first before doing max(). Please fix that or mention that. \$\endgroup\$ – perennial_noob May 23 at 18:44
  • 7
    \$\begingroup\$ Not sure how this answer got so many upvotes: it specializes to treat a use case beyond project scope (more than two inputs) and fails to handle a core acceptance criteria (identify when strings have same length). max is tidy and Pythonic, but this is a poor code review. \$\endgroup\$ – user1717828 May 24 at 13:20
28
\$\begingroup\$

Limit execution to main module

It is customary for code that starts executing a series of commands to be surrounded in a special if-block:

if __name__ == '__main__':
    ...

This prevents the code from being executed when it is imported into another module.

It's probably a good idea to put most of your code into a method or two

Particularly once you've put your code inside a main block, the multiple levels of indentation can get a little messy quickly. It helps to put some of the code into a method and then call it, rather than just have it all in sequence:

def print_longer_string(s1, s2):
    ...

string1 = input("Enter first string: ")
string2 = input("Enter second string: ")
print_longer_string(string1, string2)

Use len

len is the standard mechanism for obtaining the length of a str, as well as any other sequence type.

Reduce repetition

You can reduce your if block to just two conditions by testing for equal lengths first and using a ternary:

if len(string1) == len(string2):
      print("Both strings are equal.")
else:
      print("Larger string is:")
      print(string1 if len(string1) > len(string2) else string2)

This allows you to avoid repeating the print("Larger string is:") line without having to move that message to a variable.

Use more descriptive messages

"Both strings are equal" doesn't really describe what the program is telling you. "Larger" can also have different meanings, as well. (It could refer to lexical sorting, for example.) "The strings have equal length" and "The longer string is:" would be more explicit and less likely to cause confusion. We could differentiate between character and byte length, if that won't be clear from context, but character length is the usual assumption and is what you get from Python 3 by default.

Formatting

Read PEP8 for Python's standards on the use of spaces around parentheses, indentation length, and blank lines. Your team might define their own standards, but PEP8 is the industry default.

Final code

Putting all these together, you will get something like

def print_longer_string(s1, s2):
    if len(s1) == len(s2):
        print("The strings have equal length")
    else:
        print("The longer string is:")
        print(s1 if len(s1) > len(s2) else s2)

if __name__ == '__main__':
    s1 = input("Enter the first string: ")
    s2 = input("Enter the second string: ")
    print_longer_string(s1, s2)

You'll note I also shortened the variables down to s1 and s2. string1 is actually fine as a variable name if you prefer; I just find s1 a bit quicker to read through. You usually want meaningful variable names, but there's no semantic meaning to these variables to capture in the name since it's just two arbitrary strings, so s1 doesn't really lose anything over string1.

I also want to note that I considered separating out the printing from actually picking which string to print. I decided not to separate them because the case of equal lengths was handled differently. This fact greatly reduced any benefit we would get from separating the determination from the actual IO call. Separating them would require either having a function that returns the full string to print (which has little value since the exact message is probably dependent on the IO mechanism anyway) or introducing an extra indicator in the return value to detect the equal length case (which is a level of complexity the program does not need yet under its current requirements).

\$\endgroup\$
  • \$\begingroup\$ IIRC, in Python 3, len returns the number of codepoints, not graphemes or grapheme clusters. \$\endgroup\$ – Solomon Ucko May 24 at 13:42
  • \$\begingroup\$ @SolomonUcko: True. But there aren't any simple built-in solutions to that problem; unicodedata.normalize in NFC/NFKC mode will compose cases where an existing codepoint can express the composed form of decomposed code points, but there are some languages (East Asian IIRC) where the set of composed characters is too huge to assign a codepoint to each, so you can't combine them. I'm sure some PyPI module offers grapheme support, but it's usually allowable to compare codepoint lengths in exercises like this. \$\endgroup\$ – ShadowRanger May 24 at 16:38
  • \$\begingroup\$ @ShadowRanger Yep. BTW, I found the grapheme package. It is much less efficient to operate on graphemes, though, as they are variable-size, and operating on codepoints is often good enough. A native implementation could, however, store an array of pointers to the beginning of each grapheme, and perform operations using that. \$\endgroup\$ – Solomon Ucko May 24 at 17:41
7
\$\begingroup\$

Since Acccumulation's answer was considered too confusing, here's the same using a real Python ternary operator.

print('Equal' if len(s1) == len(s2) else 'Larger is ' + max(s1, s2, key=len))

I don't see the point in using .format for this kind of simple concatenation.

\$\endgroup\$
6
\$\begingroup\$

Building off of WeRelic and user201327 answers, if you really want to optimize for short code, you can do:

print(('Larger string is:{}'.format(max(string1,string2, key=len)),'Both strings are equal.')[len(string1)==len(string2)])

However, a more readable version would be

if len(string1)==len(string2):
       print('Both strings are equal.')
else:
    print('Larger string is:{}'.format(max(string1,string2, key=len))

Or, following JollyJoker's suggestion,

print( 'Both strings are equal.' if len(string1)==len(string2) 
        else 'Larger string is:{}'.format(max(string1,string2, key=len)))

Breaking down the short version:

max(string1,string2, key=len) returns the larger string, as measured by length

('Larger string is:{}'.format(max(string1,string2, key=len)) Takes the larger of the two strings, and inserts it into the string 'Larger string is:

('Larger string is:{}'.format(max(string1,string2, key=len)),'Both strings are equal.') creates tuple where the first value says what the larger string is, and the second element says they're equal

len(string1)==len(string2) returns a boolean based on whether the strings are equal length.

[len(string1)==len(string2)] takes one of the elements of the tuple, according to the value of len(string1)==len(string2). This coerces the boolean into an integer: False is considered to be 0 and retrieves the Larger string is: element. True is considered to be 1, and retrieves the 'Both strings are equal.' element.

\$\endgroup\$
  • \$\begingroup\$ Judging by the comments, this answer except using a if condition else b should be the correct one. So, print('Both strings are equal.' if len(string1)==len(string2) else 'Larger string is:{}'.format(max(string1,string2, key=len) ? \$\endgroup\$ – JollyJoker May 23 at 8:29
  • 1
    \$\begingroup\$ -1, this is technically correct, but the functionality is obfuscated to the point where you need multiple paragraphs to explain how it works. This isn't code golf; rather, the code should be easy-to-read and self-explanatory. \$\endgroup\$ – crunch May 23 at 9:35
  • \$\begingroup\$ @crunch Well, the OP did indicate that they were asking for a short version. And I think it's a bit perverse to penalize me for spending several paragraphs explaining the code. Much of the length was due to explaining things at a basic level, such as linking to an explanation of coercion. But I've edited to make it more clear that this isn't necessarily the best way of doing it. \$\endgroup\$ – Acccumulation May 23 at 13:55
  • \$\begingroup\$ @JollyJoker In Python, it's if condition a else b, rather than a if condition else b. \$\endgroup\$ – Acccumulation May 23 at 13:56
  • 4
    \$\begingroup\$ @Acccumulation book.pythontips.com/en/latest/ternary_operators.html It's real, just looks like a switched around if-else \$\endgroup\$ – JollyJoker May 23 at 13:57
2
\$\begingroup\$

Long live the Ternary:

def print_longer(s,s2):
    # return (s,s2)[len(s)<len(s2)] if you don't want to print within the function.
    print( ( s, s2 )[ len(s) < len(s2) ] )

Explanation:

if-else statements are clean, but they're verbose. A ternary operation would reduce this to a one-liner.

The format is as follows: (result_if_false,result_if_true)[comparison]

What is happening is that (s,s2) is creating a tuple of the two strings. len(s)<len(s2) then compares the two, and because they're within square brackets []; the boolean result is casted to an integer index.

Since you can only have a 0 or 1 result, this returns s if it is larger than s2, and vice-versa.

EDIT: This returns s if both strings are of equal lengths.

\$\endgroup\$
  • 4
    \$\begingroup\$ It doesn't properly handle the case of "Both strings are equal." but upvoted anyways because it's a nice approach \$\endgroup\$ – Andres May 22 at 18:37
  • 3
    \$\begingroup\$ The expression in your answer is not a ternary operator. By definition, a ternary operator takes 3 arguments. Your code uses binary operators instead. \$\endgroup\$ – Roland Illig May 22 at 21:25
  • 4
    \$\begingroup\$ Python's ternary is a if condition else b, not your tuple-indexing. Your own link calls your method obscure. It should only be seen as a holdover from very old versions of Python that don't support the if/else syntax (pre-2.5). \$\endgroup\$ – jpmc26 May 22 at 23:03
  • 8
    \$\begingroup\$ I don't think this is appropriate. Python doesn't support C style ternaries and attempting to replicate it by indexing a tuple is not pythonic and so should not be suggested \$\endgroup\$ – Sirens May 23 at 4:43
  • 5
    \$\begingroup\$ @Sirens The if/else syntax is equivalent to C style ternaries, in that it only executes the selected expression. \$\endgroup\$ – jpmc26 May 23 at 6:06
0
\$\begingroup\$

Here's how I would find the longest strings in a list of strings:

import itertools

def longest_string(strings):
    if not strings:
        return []

    strings_by_length = itertools.groupby(strings, len)
    maximum_length = max(strings_by_length.keys())
    return strings_by_length[maximum_length]
\$\endgroup\$
  • \$\begingroup\$ This does not behave as the original post does for the case of strings of equal length. \$\endgroup\$ – jpmc26 May 29 at 13:17
-1
\$\begingroup\$

You can immediately assume that the first one is larger and then reassign it to the second one if that one is larger, like this:

larger = input("Enter first string: ")
string2 = input("Enter second string: ")
if (len(string2) > len(larger)):
    larger = string2
print(larger)
\$\endgroup\$
  • 1
    \$\begingroup\$ Brackets are not necessary. \$\endgroup\$ – Acccumulation May 23 at 16:05
  • 1
    \$\begingroup\$ Was your code supposed to be C or Python? At the moment, it's neither. \$\endgroup\$ – Mast May 23 at 16:58
  • \$\begingroup\$ @Mast Sorry, I don't know either. I'm just pretending. \$\endgroup\$ – stackzebra May 23 at 17:58
  • \$\begingroup\$ @Mast Fixed now. \$\endgroup\$ – stackzebra May 24 at 7:08
  • \$\begingroup\$ This does not behave as the original post does for the case of strings of equal length. \$\endgroup\$ – jpmc26 May 29 at 13:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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