0

I just read a post here about how to filter values from two lists.python: How to remove values from 2 lists based on what's in 1 list

I tried to write some code myself using for and zip, but the result is wrong, can anyone help me understand this?

xVar = [1, 2, 3, 4, 5, 6, 7]
yVar = [11, 22, 33, 44, 55, 66, 77]

z = zip(xVar, yVar)
zFiltered = [(x, y) for x, y in z if y < 50]
print zFiltered # Correct, prints [(1, 11), (2, 22), (3, 33), (4, 44)]

for (x, y) in z:
    if y > 50:
        z.remove((x, y))

print z # Wrong, prints [(1, 11), (2, 22), (3, 33), (4, 44), (6, 66)]

Why is this happening? I tried to use pdb and found that after z.remove((5, 55)), (x, y)'s value is (7, 77), (6, 66) is skiped, I assume that for is assigning (x, y) via indexs, removing (5, 55) will violate the original index, if so, how can I do the same thing here with for statements? and is there any other pitfalls here?

Community
  • 1
  • 1
shengy
  • 9,461
  • 4
  • 37
  • 61

2 Answers2

3

Try this list comprehension which will select elements in your zipped list based on a condition (basically the opposite of removing elements from the list that do not satisfy this condition, but it will yield the same results).

z = [(x, y) for (x, y) in z if y <= 50]

If you want to delete elements in a list while iterating over it, you must iterate backwards because every time you delete an item in a list, all of the elements that have higher indexes in the list get shifted downward to lower indexes.

You can do such an iteration like so:

for i in xrange(len(z) -1, -1, -1):
    if z[i][1] > 50:
        z.remove(z[i])
Shashank
  • 13,713
  • 5
  • 37
  • 63
2

The problem is that when you remove an item from a list while iterating over it, its internal structure gets out of whack, and items are skipped. The easy way around this issue is to make a copy of the list by adding [:].

for (x, y) in z[:]:
    ...

Now you can make changes to the original list as it is not being iterated over.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237