Search code examples
pythonlistpython-3.5anagram

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.


Solution

  • 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.