2

So, I don't understand why my output returns [1, 1, 3, 1, 3], when what I want, and thought, was [1,1,1].

the_list = [1, 2, 1, 2, 3, 1, 2, 3, 4]
target = 1

def keep(the_list, target):
    index = 0
    for x in the_list:
        if x != target:
            del the_list[index]
        else:
            pass

        index += 1
print(the_list)
pythonstud
  • 41
  • 5
  • The problem with your code is that you are deleting according to the increment in index and not actually deleting the value in `the_list` and not `x != target`. As mentioned in my answer below, it's gonna be much easier if you use a list comprehension. – BernardL Oct 23 '18 at 01:43
  • see https://stackoverflow.com/questions/1207406/how-to-remove-items-from-a-list-while-iterating – Paul Rooney Oct 23 '18 at 01:57

2 Answers2

1

It is gonna be much cleaner if you use list comprehension to check your list elements and return only what you need.

the_list = [1, 2, 1, 2, 3, 1, 2, 3, 4]
target = 1

new_list = [i for i in the_list if i == target]

List comprehensions are much more readable because its intent is explicit. A loop maybe used to do a lot of different things like more complex aggregations or more processing tasks. In contrast, a list comprehension is only meant to do one thing which is build a new list.

BernardL
  • 5,162
  • 7
  • 28
  • 47
1

When you delete an item from a list at a specific index, all the items after the specified index get moved forward by 1 since there can be no gap in a list, so after you delete the 2 at index 1, for example, in the next iteration x would become 2 at the index 2 when it used to be at index 3, so your own index variable would be pointing to the wrong item.

To delete items from a list in-place, you should instead count backwards from the end of the list so that the re-indexing of the list after you delete an item won't affect the accuracy of your index counter:

def keep(the_list, target):
    index = len(the_list) - 1
    while index >= 0:
        if the_list[index] != target:
            del the_list[index]
        index -= 1

so that:

the_list = [1, 2, 1, 2, 3, 1, 2, 3, 4]
target = 1
keep(the_list, target)
print(the_list)

would output:

[1, 1, 1]

But keep in mind that deleting an item from a list is inherently inefficient since it has an average time complexity of O(n) for having to move items after the given index, so deleting multiple items from a list becomes quadratic in complexity. It's much more efficient to use list comprehension to construct a new list from the old one by retaining only items that are equal to the target value. So even though the code above shows you how to correctly delete items from a list in-place, you should not actually use it in any production code.

blhsing
  • 91,368
  • 6
  • 71
  • 106
  • While you code may work at the moment, it's inherently dependent on the internal details of how Python `list`s are (currently) implemented. I think you should emphasize the fact that using this "trick" should never be used for that reason (and not just because it might be relatively inefficient), – martineau Oct 23 '18 at 02:03
  • I don't think there's any implementation of a list/array in any language where the above logic of counting backwards from the end of a list while deleting items would fail. Please point me to one if there exists any. – blhsing Oct 23 '18 at 02:06