Alex Alex - 1 month ago 13
Python Question

Python 3 Index Error

Consider the following code:

def anadist(string1, string2):
string1_list = []
string2_list = []

for i in range(len(string1)):
string1_list.append(string1[i])
for i in range(len(string2)):
string2_list.append(string2[i])

# Test returns for checking
# return (string1_list,string2_list)
# return len(string1_list)
# return len(string2_list)

for i in range(0,len(string1_list)):
try:
if (string1_list[i]) in string2_list:
com = string1_list.pop(i)
com_index = string2_list.index(com)
string2_list.pop(com_index)
else:
pass
except ValueError:
pass
return string1_list


def main():
str1 = input("Enter string #1 >>> ")
str2 = input("Enter string #2 >>> ")
result = anadist(str1, str2)
print(result)

#Boilerplate Check
if __name__ == "__main__":
main()


Running in Python 3.5.2 raises an IndexError:

Traceback (most recent call last):
File "E:\CSE107L\Practice\anadist.py", line 34, in <module>
main()
File "E:\CSE107L\Practice\anadist.py", line 29, in main
result = anadist(str1, str2)
File "E:\CSE107L\Practice\anadist.py", line 15, in anadist
if (string1_list[i]) in string2_list:
IndexError: list index out of range


And I can't find what is going wrong. I wrote another code similar and that works:

def main():
lst = [1,2,3,4,5]
lst2 = [5,6,7,8,9]
for i in range(len(lst)):
if lst[i] in lst2:
com = lst.pop(i)
lst2_index = lst2.index(com)
lst2.pop(lst2_index)
else:
pass
print(lst)

if __name__ == "__main__":
main()


I feel the error is coming from the way I am forming
string1_list
. This code is for how many steps it takes to form an anagram of a pair of words.

Answer

In some cases, you are shortening string_list1 while you're iterating over it:

if (string1_list[i]) in string2_list:
    com = string1_list.pop(i)  # string1_list gets shorter here

However, your range doesn't change. It's still going to go from 0 until it counts up to the original length of string1_list (exclusive). This will cause the IndexError any time string1_list.pop(i) is called.

One possible solution would be to use a while loop instead:

i = 0
while i < len(string1_list):
    try:
        if string1_list[i] in string2_list:
            com = string1_list.pop(i)
            com_index = string2_list.index(com)
            string2_list.pop(com_index)
        else:
            pass
    except ValueError:
        pass
    i += 1

This will cause the loop termination condition to be checked after each iteration. If you remove some elements from string1_list, it'll still be OK because the loop will terminate before i gets big enough to overrun the bounds of it's container.