Reputation: 16940
In the following two examples, I am trying to remove negative element from a list using two different types of looping.
First I tried by using normal looping for i in list
but in this case when i do list1.remove(elm)
the size of the list is reduce by one element. So, in the next loop the second element moves to the first element position. So, the second element is missed from testing if elm < 0
in the next loop. So, it doesn't remove all the negative element from the list.
Secondly, I tried using slicing. What i understood from slicing is it creates a temporary list. So, when i do for i in list2[:]
it creates a new temporary list2 = [-1,3,-2,-5,4,7,8]
but still I didn't get a clear picture, how it works. It removes all the negative element.
#!/usr/env/bin
# remove all the negative value from the list.
list1 = [-1,3,-2,-5,4,7,8]
list2 = [-1,3,-2,-5,4,7,8]
# Looping through the list.
for elm in list1:
if elm < 0:
list1.remove(elm)
print 'list1: ', list1
# Looping through the list using slice.
for elm in list2[:]:
if elm < 0:
list2.remove(elm)
print 'list2: ', list2
Output:-python slice.py
list1: [3, -5, 4, 7, 8]
list2: [3, 4, 7, 8]
Upvotes: 2
Views: 389
Reputation: 83235
The problem is that iterating through a list and modifying it at the same time can produce some bad results.
For example, if you are at index 2, and remove a negative number there, the value at index 3 will move to index 2. On the next iteration, you will go to index 3, but skip the value at the old index 3 (new index 2).
The slice creates a copy, and so it is untouched, even as you remove negatives.
FYI, another possibility is
filter(lambda x: x >= 0, list2)
or
[x for x in list2 if x >= 0]
EDIT:
Here are the iterations.
(index) list1 elem
0 [-1,3,-2,-5,4,7,8] -1
1 [3,-2,-5,4,7,8] -2
2 [3,-5,4,7,8] 4
3 [3,4,7,8] 8
Do you see how we missed several values because we were iterating and modifying the same thing?
Now with the slice, which creates a copy,
(index) (copy) list2 elem
0 [-1,3,-2,-5,4,7,8] [-1,3,-2,-5,4,7,8] -1
1 [-1,3,-2,-5,4,7,8] [3,-2,-5,4,7,8] 3
2 [-1,3,-2,-5,4,7,8] [3,-2,-5,4,7,8] -2
3 [-1,3,-2,-5,4,7,8] [3,-5,4,7,8] -5
4 [-1,3,-2,-5,4,7,8] [3,4,7,8] 4
5 [-1,3,-2,-5,4,7,8] [3,4,7,8] 7
6 [-1,3,-2,-5,4,7,8] [3,4,7,8] 8
We iterate through all seven values, without skipping any.
FYI, list2[:]
is equivalent to list2[0:len(list2):1]
or list(list2)
.
Upvotes: 4
Reputation: 26333
slicing operator copies the list you are operating on, leading to accurate result.
Upvotes: 0
Reputation: 59974
In list one, python is removing an item while you're iterating through it, which can lead to unexpected results, as you have seen. Watch:
for elm in list1:
if elm < 0:
list1.remove(elm)
print list1
This will print:
[3, -2, -5, 4, 7, 8]
[3, -5, 4, 7, 8]
[3, -5, 4, 7, 8]
[3, -5, 4, 7, 8]
[3, -5, 4, 7, 8]
So when you python goes back to for elm in list1
after removing an item, it will not go to the one after that, but the one before (3
). Hence the loop.
When you make a copy, list2[:]
, you're not actually iterating over list2
but just a copy, hence when you remove items, you're not removing anything what you're iterating over.
Upvotes: 1
Reputation: 184131
I'd go with a list comprehension here to either create a new list containing the elements you want to keep:
numbers = [-1, 3, -2, -5, 4, 7, 8]
positive = [n for n in numbers if n >= 0]
or to reassign the new sequence into the existing one via slice assignment:
numbers = [-1, 3, -2, -5, 4, 7, 8]
numbers[:] = (n for n in numbers if n >= 0)
Upvotes: 1