Skip to main content
Engineering LibreTexts

9.18: When Deleting Items in a List, Iterate Over the List in Reverse

  • Page ID
    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Deleting squirrel and grass objects is done with the del operator. However, notice that the for loop on line 156 and 159 pass arguments to the range() function so that the numbering starts at the index of the last item and then decrements by -1 (unlike incrementing by 1 as it normally does) until it reaches the number -1. We are iterating backwards over the list’s indexes compared to how it is normally done. This is done because we are iterating over the list that we are also deleting items from.

    To see why this reverse order is needed, say we had the following list value:

    animals = ['cat', 'mouse', 'dog', 'horse']

    So we wanted to write code to delete any instances of the string 'dog' from this list. We might think to write out code like this:

    for i in range(len(animals)):
        if animals[i] == 'dog':
            del animals[i]

    But if we ran this code, we would get an IndexError error that looks like this:

    Traceback (most recent call last):
        File "<stdin>", line 2, in <module>
    IndexError: list index out of range 

    To see why this error happens, let’s walk through the code. First, the animals list would be set to ['cat', 'mouse', 'dog', 'horse']and len(animals) would return 4.This means that the call to range(4) would cause the for loop to iterate with the values 0, 1, 2, and 3.

    When the for loop iterates with i set to 2, the if statement’s condition will be True and the del animals[i] statement will delete animals[2]. This means that afterwards the animals list will be ['cat', 'mouse', 'horse']. The indexes of all the items after 'dog' are all shifted down by one because the 'dog' value was removed.

    But on the next iteration through the for loop, i is set to 3. But animals[3] is out of bounds because the valid indexes of the animals list is no longer 0 to 3 but 0 to 2. The original call to range() was for a list with 4 items in it. The list changed in length, but the for loop is set up for the original length.

    However, if we iterate from the last index of the list to 0, we don’t run into this problem. The following program deletes the 'dog' string from the animals list without causing an IndexError error:

    animals = ['cat', 'mouse', 'dog', 'horse']
    for i in range(len(animals) - 1, -1, -1):
        if animals[i] == 'dog':
            del animals[i] 

    The reason this code doesn’t cause an error is because the for loop iterates over 3, 2, 1, and 0. On the first iteration, the code checks if animals[3] is equal to 'dog'. It isn’t (animals[3] is 'horse') so the code moves on to the next iteration. Then animals[2] is checked if it equals 'dog'. It does, so animals[2] is deleted.

    After animals[2] is deleted, the animals list is set to ['cat', 'mouse', 'horse']. On the next iteration, i is set to 1. There is a value at animals[1] (the 'mouse' value), so no error is caused. It doesn’t matter that all the items in the list after 'dog' have shifted down by one, because since we started at the end of the list and are going towards the front, all of those items have already been checked.

    Similarly, we can delete grass and squirrel objects from the grassObjs and squirrelObjs lists without error because the for loop on lines 156 and 159 iterate in reverse order.

    This page titled 9.18: When Deleting Items in a List, Iterate Over the List in Reverse is shared under a CC BY-NC-SA 3.0 license and was authored, remixed, and/or curated by Al Sweigart via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.