i am trying to write a function that will analyze a string to check if it contains two of the same letter in a row. i wrote the function for it but it doesnt work, any idea why?
import itertools
def double_letters(word):
word = list(word)
for index, item in enumerate(word):
for next_item in word[index 1:]:
if item == next_item:
return True
else:
return False
CodePudding user response:
You're doing a few weird things here, but the main one is the secondary loop. Why do you need that if you have the index? You know the next index is just 1 and you don't even use both (index & value) here.
The second biggest problem is returning false if the characters don't match; this will return the matching result only for the first two characters, but you're looking for the first match, so you should only return on a match. You can use a variable to track this, but you can also simply return true when needed and return false if that condition is never met.
Last, you don't need enumerate, although it's fine. For this approach I would just enumerate to the 2nd last index (enumerate(word[0:len(word)-1]
). Or, don't use enumerate like in the example below:
import itertools
def double_letters(word):
word = list(word)
for index in range(len(word)-1):
if word[index] == word[index 1]:
return True
return False;
print(double_letters('world hello '))
CodePudding user response:
There are three problems with this function, and two of them are on this line:
for next_item in word[index 1:]:
- You're iterating over all of the letters following
item
, when the onlynext_item
you're concerned with is the immediate next letter (i.e.word[index 1]
, not the entire sliceword[index 1:]
word[index 1]
is going to walk off the edge of the word, leaving you with nonext_item
to compare. To fix this, you could check to see if you're at the end and special-case it, or you could just iterate overword[:-1]
(everything but the last letter) in the outer loop.
The final problem (and the one that's causing your immediate bug) is with this if/else
:
if item == next_item:
return True
else:
return False
No matter what, you will immediately return upon making the first comparison -- hence this function will only count a double letter if it's the first two letters. It'll never get to compare anything else. To fix this, remove the else
, and have the return False
happen at the very end of the function, so that we only return False if we haven't found a reason to return True.
All together:
def double_letters(word):
for index, item in enumerate(word[:-1]):
next_item = word[index 1]
if item == next_item:
return True
return False
assert double_letters("hello")
assert not double_letters("abcdef")
A solution with itertools
might be to use groupby
, which groups an iterable into identical items; you can check to see if any of the groups are longer than 1 item like this:
import itertools
def double_letters(word):
for _, g in itertools.groupby(word):
if len(list(g)) > 1:
return True
return False
But the easiest way to write this type of sequential-item check is to zip
the iterable with a slice of itself, like this:
def double_letters(word):
for item, next_item in zip(word, word[1:]):
if item == next_item:
return True
return False
or more simply, using any
:
def double_letters(word):
return any(item == next_item for item, next_item in zip(word, word[1:]))
CodePudding user response:
If you don't have an equality once, you cannot stop directly by returning false. You need to wait the end. Also don't need a nested loop, use zip
to iterate on 2 consecutive chars of a string
def double_letters(word):
for current_ch, next_ch in zip(word, word[1:]):
if current_ch == next_ch:
return True
return False