This question already has answers here:
Strange result when removing item from a list while iterating over it
(8 answers)
Closed 7 years ago.
For quite a bit of time now I have been trying to figure out a way to loop through a list and remove the current item that I'm at. I can't seem to get this working as I would like it to. It loops just 1 time through, but I wanted it to loop 2 times. When I remove the removal line - it loops 2 times.
a = [0, 1]
for i in a:
z = a
print z.remove(i)
The output:
[1]
The output that I was expecting:
[1]
[0]
You're changing the list while iterating over it -- z = a doesn't make a copy, it just points z at the same place a points.
Try
for i in a[:]: # slicing a list makes a copy
print i # remove doesn't return the item so print it here
a.remove(i) # remove the item from the original list
or
while a: # while the list is not empty
print a.pop(0) # remove the first item from the list
If you don't need an explicit loop, you can remove items that match a condition with a list comprehension:
a = [i for i in a if i] # remove all items that evaluate to false
a = [i for i in a if condition(i)] # remove items where the condition is False
It is bad practice modify a list while you're looping through it†. Create a copy of the list:
oldlist = ['a', 'b', 'spam', 'c']
newlist = [x for x in oldlist if x != 'spam']
To modify the original list, write the copy back in-place with a slice assignment:
oldlist[:] = [x for x in oldlist if x != 'spam']
† For a gist of why this might be bad practice, consider the implementation details of what goes on with the iterator over the sequence when the sequence changes during iteration. If you've removed the current item, should the iterator point to the next item in the original list or to the next item in the modified list? What if your decision procedure instead removes the previous (or next) item to the current?
The problem is that you're modifying a with remove so the loop exits because the index is now past the end of it.
Don't try to remove multiple items of a list while looping the list. I think it's a general rule you should follow not only in python but also in other programming languages as well.
You could add the item to be removed into a separate list. And then remove all objects in that new list from the original list.
Related
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 6 months ago.
I am trying to write simple code to print an item of a list and remove it after printing:
list = ['a', 'b', 'c']
for i in list:
print(i)
list.remove(i)
But output is weird:
a
c
Why is output thay way?
When you iterate over a list, you get the items in order of their indices (item 0, item 1, item 2, etc). When you remove an item from a list, the indices of all the items after that shift by one.
In the first iteration, the list is ['a', 'b', 'c'], and i is list[0].
During the first iteration, you remove 'a'.
In the second iteration, the list is ['b', 'c'], and i is list[1]. You get 'c' instead of 'b' because 'c' is now at index 1.
If you want to remove each item as you iterate, the better approach would be to iterate in a while loop as long as the list contains items, and pop as you print:
my_list = ['a', 'b', 'c']
while my_list:
print(my_list.pop(0))
In many cases, it's better to do the thing you want to do in the iteration, and then clear the list:
for i in my_list:
print(i)
my_list.clear()
You're currently iterating while removing the items, if you want alter the list while reading it then probably you want to use the length "as index":
list = ['a', 'b', 'c']
while len(list):
# pop does what you want: read the element at index [i] and remove it from the list
print(list.pop(0))
Output:
a
b
c
Explanation
the reason the output seems strange it's because you are removing items when iterating over a list.
the problem here is that python iterates checking for the index.
Consider this example:
lst = [32,43,2]
for x in lst:
lst.pop(0)
print(x,lst)
Outputs
32 [43, 2]
2 [2]
here you can see the problem. in the first iteration it took the first item that was removed, all ok. The problem starts with the second iteration.
The iterator thinks the index to go is 1 (2nd element) but it's actually the 1st since the first element was removed.
You can fix it also by iterating the reversed list as the index cannot change.
also see this question for more information
Possible solutions
You should iterate over a copy instead:
for x in mylist.copy():
mylist.remove(x)
You could also use a while loop and list.pop.
while mylist:
print(mylist.pop(0))
Advice
Before leaving, I would like to give some advice.
Don't use builtin as variable names, it causes confusion and could cause conflict in your code if it uses those builtin names.
I would advice to clear the list after the loop using the list.clear() method.
Use the list.pop method if you want to know a value and remove it at the same time.
Useful links
python datastructures official documentation
python list w3school
linuxhint python list methods
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 7 years ago.
I have a list
a = ["a", "b", "c", "d", "e"]
I want to remove elements in this list in a for loop like below:
for item in a:
print(item)
a.remove(item)
But it doesn't work. What can I do?
You are not permitted to remove elements from the list while iterating over it using a for loop.
The best way to rewrite the code depends on what it is you're trying to do.
For example, your code is equivalent to:
for item in a:
print(item)
a[:] = []
Alternatively, you could use a while loop:
while a:
print(a.pop())
I'm trying to remove items if they match a condition. Then I go to next item.
You could copy every element that doesn't match the condition into a second list:
result = []
for item in a:
if condition is False:
result.append(item)
a = result
Alternatively, you could use filter or a list comprehension and assign the result back to a:
a = filter(lambda item:... , a)
or
a = [item for item in a if ...]
where ... stands for the condition that you need to check.
Iterate through a copy of the list:
>>> a = ["a", "b", "c", "d", "e"]
>>> for item in a[:]:
print(item)
if item == "b":
a.remove(item)
a
b
c
d
e
>>> print(a)
['a', 'c', 'd', 'e']
As other answers have said, the best way to do this involves making a new list - either iterate over a copy, or construct a list with only the elements you want and assign it back to the same variable. The difference between these depends on your use case, since they affect other variables for the original list differently (or, rather, the first affects them, the second doesn't).
If a copy isn't an option for some reason, you do have one other option that relies on an understanding of why modifying a list you're iterating breaks. List iteration works by keeping track of an index, incrementing it each time around the loop until it falls off the end of the list. So, if you remove at (or before) the current index, everything from that point until the end shifts one spot to the left. But the iterator doesn't know about this, and effectively skips the next element since it is now at the current index rather than the next one. However, removing things that are after the current index doesn't affect things.
This implies that if you iterate the list back to front, if you remove an item at the current index, everything to it's right shifts left - but that doesn't matter, since you've already dealt with all the elements to the right of the current position, and you're moving left - the next element to the left is unaffected by the change, and so the iterator gives you the element you expect.
TL;DR:
>>> a = list(range(5))
>>> for b in reversed(a):
if b == 3:
a.remove(b)
>>> a
[0, 1, 2, 4]
However, making a copy is usually better in terms of making your code easy to read. I only mention this possibility for sake of completeness.
import copy
a = ["a", "b", "c", "d", "e"]
b = copy.copy(a)
for item in a:
print(item)
b.remove(item)
a = copy.copy(b)
Works: to avoid changing the list you are iterating on, you make a copy of a, iterate over it and remove the items from b. Then you copy b (the altered copy) back to a.
How about creating a new list and adding elements you want to that new list. You cannot remove elements while iterating through a list
Probably a bit late to answer this but I just found this thread and I had created my own code for it previously...
list = [1,2,3,4,5]
deleteList = []
processNo = 0
for item in list:
if condition:
print(item)
deleteList.insert(0, processNo)
processNo += 1
if len(deleteList) > 0:
for item in deleteList:
del list[item]
It may be a long way of doing it but seems to work well. I create a second list that only holds numbers that relate to the list item to delete. Note the "insert" inserts the list item number at position 0 and pushes the remainder along so when deleting the items, the list is deleted from the highest number back to the lowest number so the list stays in sequence.
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 7 years ago.
I have a list
a = ["a", "b", "c", "d", "e"]
I want to remove elements in this list in a for loop like below:
for item in a:
print(item)
a.remove(item)
But it doesn't work. What can I do?
You are not permitted to remove elements from the list while iterating over it using a for loop.
The best way to rewrite the code depends on what it is you're trying to do.
For example, your code is equivalent to:
for item in a:
print(item)
a[:] = []
Alternatively, you could use a while loop:
while a:
print(a.pop())
I'm trying to remove items if they match a condition. Then I go to next item.
You could copy every element that doesn't match the condition into a second list:
result = []
for item in a:
if condition is False:
result.append(item)
a = result
Alternatively, you could use filter or a list comprehension and assign the result back to a:
a = filter(lambda item:... , a)
or
a = [item for item in a if ...]
where ... stands for the condition that you need to check.
Iterate through a copy of the list:
>>> a = ["a", "b", "c", "d", "e"]
>>> for item in a[:]:
print(item)
if item == "b":
a.remove(item)
a
b
c
d
e
>>> print(a)
['a', 'c', 'd', 'e']
As other answers have said, the best way to do this involves making a new list - either iterate over a copy, or construct a list with only the elements you want and assign it back to the same variable. The difference between these depends on your use case, since they affect other variables for the original list differently (or, rather, the first affects them, the second doesn't).
If a copy isn't an option for some reason, you do have one other option that relies on an understanding of why modifying a list you're iterating breaks. List iteration works by keeping track of an index, incrementing it each time around the loop until it falls off the end of the list. So, if you remove at (or before) the current index, everything from that point until the end shifts one spot to the left. But the iterator doesn't know about this, and effectively skips the next element since it is now at the current index rather than the next one. However, removing things that are after the current index doesn't affect things.
This implies that if you iterate the list back to front, if you remove an item at the current index, everything to it's right shifts left - but that doesn't matter, since you've already dealt with all the elements to the right of the current position, and you're moving left - the next element to the left is unaffected by the change, and so the iterator gives you the element you expect.
TL;DR:
>>> a = list(range(5))
>>> for b in reversed(a):
if b == 3:
a.remove(b)
>>> a
[0, 1, 2, 4]
However, making a copy is usually better in terms of making your code easy to read. I only mention this possibility for sake of completeness.
import copy
a = ["a", "b", "c", "d", "e"]
b = copy.copy(a)
for item in a:
print(item)
b.remove(item)
a = copy.copy(b)
Works: to avoid changing the list you are iterating on, you make a copy of a, iterate over it and remove the items from b. Then you copy b (the altered copy) back to a.
How about creating a new list and adding elements you want to that new list. You cannot remove elements while iterating through a list
Probably a bit late to answer this but I just found this thread and I had created my own code for it previously...
list = [1,2,3,4,5]
deleteList = []
processNo = 0
for item in list:
if condition:
print(item)
deleteList.insert(0, processNo)
processNo += 1
if len(deleteList) > 0:
for item in deleteList:
del list[item]
It may be a long way of doing it but seems to work well. I create a second list that only holds numbers that relate to the list item to delete. Note the "insert" inserts the list item number at position 0 and pushes the remainder along so when deleting the items, the list is deleted from the highest number back to the lowest number so the list stays in sequence.
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 7 years ago.
I have a list
a = ["a", "b", "c", "d", "e"]
I want to remove elements in this list in a for loop like below:
for item in a:
print(item)
a.remove(item)
But it doesn't work. What can I do?
You are not permitted to remove elements from the list while iterating over it using a for loop.
The best way to rewrite the code depends on what it is you're trying to do.
For example, your code is equivalent to:
for item in a:
print(item)
a[:] = []
Alternatively, you could use a while loop:
while a:
print(a.pop())
I'm trying to remove items if they match a condition. Then I go to next item.
You could copy every element that doesn't match the condition into a second list:
result = []
for item in a:
if condition is False:
result.append(item)
a = result
Alternatively, you could use filter or a list comprehension and assign the result back to a:
a = filter(lambda item:... , a)
or
a = [item for item in a if ...]
where ... stands for the condition that you need to check.
Iterate through a copy of the list:
>>> a = ["a", "b", "c", "d", "e"]
>>> for item in a[:]:
print(item)
if item == "b":
a.remove(item)
a
b
c
d
e
>>> print(a)
['a', 'c', 'd', 'e']
As other answers have said, the best way to do this involves making a new list - either iterate over a copy, or construct a list with only the elements you want and assign it back to the same variable. The difference between these depends on your use case, since they affect other variables for the original list differently (or, rather, the first affects them, the second doesn't).
If a copy isn't an option for some reason, you do have one other option that relies on an understanding of why modifying a list you're iterating breaks. List iteration works by keeping track of an index, incrementing it each time around the loop until it falls off the end of the list. So, if you remove at (or before) the current index, everything from that point until the end shifts one spot to the left. But the iterator doesn't know about this, and effectively skips the next element since it is now at the current index rather than the next one. However, removing things that are after the current index doesn't affect things.
This implies that if you iterate the list back to front, if you remove an item at the current index, everything to it's right shifts left - but that doesn't matter, since you've already dealt with all the elements to the right of the current position, and you're moving left - the next element to the left is unaffected by the change, and so the iterator gives you the element you expect.
TL;DR:
>>> a = list(range(5))
>>> for b in reversed(a):
if b == 3:
a.remove(b)
>>> a
[0, 1, 2, 4]
However, making a copy is usually better in terms of making your code easy to read. I only mention this possibility for sake of completeness.
import copy
a = ["a", "b", "c", "d", "e"]
b = copy.copy(a)
for item in a:
print(item)
b.remove(item)
a = copy.copy(b)
Works: to avoid changing the list you are iterating on, you make a copy of a, iterate over it and remove the items from b. Then you copy b (the altered copy) back to a.
How about creating a new list and adding elements you want to that new list. You cannot remove elements while iterating through a list
Probably a bit late to answer this but I just found this thread and I had created my own code for it previously...
list = [1,2,3,4,5]
deleteList = []
processNo = 0
for item in list:
if condition:
print(item)
deleteList.insert(0, processNo)
processNo += 1
if len(deleteList) > 0:
for item in deleteList:
del list[item]
It may be a long way of doing it but seems to work well. I create a second list that only holds numbers that relate to the list item to delete. Note the "insert" inserts the list item number at position 0 and pushes the remainder along so when deleting the items, the list is deleted from the highest number back to the lowest number so the list stays in sequence.
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 7 years ago.
I have a list
a = ["a", "b", "c", "d", "e"]
I want to remove elements in this list in a for loop like below:
for item in a:
print(item)
a.remove(item)
But it doesn't work. What can I do?
You are not permitted to remove elements from the list while iterating over it using a for loop.
The best way to rewrite the code depends on what it is you're trying to do.
For example, your code is equivalent to:
for item in a:
print(item)
a[:] = []
Alternatively, you could use a while loop:
while a:
print(a.pop())
I'm trying to remove items if they match a condition. Then I go to next item.
You could copy every element that doesn't match the condition into a second list:
result = []
for item in a:
if condition is False:
result.append(item)
a = result
Alternatively, you could use filter or a list comprehension and assign the result back to a:
a = filter(lambda item:... , a)
or
a = [item for item in a if ...]
where ... stands for the condition that you need to check.
Iterate through a copy of the list:
>>> a = ["a", "b", "c", "d", "e"]
>>> for item in a[:]:
print(item)
if item == "b":
a.remove(item)
a
b
c
d
e
>>> print(a)
['a', 'c', 'd', 'e']
As other answers have said, the best way to do this involves making a new list - either iterate over a copy, or construct a list with only the elements you want and assign it back to the same variable. The difference between these depends on your use case, since they affect other variables for the original list differently (or, rather, the first affects them, the second doesn't).
If a copy isn't an option for some reason, you do have one other option that relies on an understanding of why modifying a list you're iterating breaks. List iteration works by keeping track of an index, incrementing it each time around the loop until it falls off the end of the list. So, if you remove at (or before) the current index, everything from that point until the end shifts one spot to the left. But the iterator doesn't know about this, and effectively skips the next element since it is now at the current index rather than the next one. However, removing things that are after the current index doesn't affect things.
This implies that if you iterate the list back to front, if you remove an item at the current index, everything to it's right shifts left - but that doesn't matter, since you've already dealt with all the elements to the right of the current position, and you're moving left - the next element to the left is unaffected by the change, and so the iterator gives you the element you expect.
TL;DR:
>>> a = list(range(5))
>>> for b in reversed(a):
if b == 3:
a.remove(b)
>>> a
[0, 1, 2, 4]
However, making a copy is usually better in terms of making your code easy to read. I only mention this possibility for sake of completeness.
import copy
a = ["a", "b", "c", "d", "e"]
b = copy.copy(a)
for item in a:
print(item)
b.remove(item)
a = copy.copy(b)
Works: to avoid changing the list you are iterating on, you make a copy of a, iterate over it and remove the items from b. Then you copy b (the altered copy) back to a.
How about creating a new list and adding elements you want to that new list. You cannot remove elements while iterating through a list
Probably a bit late to answer this but I just found this thread and I had created my own code for it previously...
list = [1,2,3,4,5]
deleteList = []
processNo = 0
for item in list:
if condition:
print(item)
deleteList.insert(0, processNo)
processNo += 1
if len(deleteList) > 0:
for item in deleteList:
del list[item]
It may be a long way of doing it but seems to work well. I create a second list that only holds numbers that relate to the list item to delete. Note the "insert" inserts the list item number at position 0 and pushes the remainder along so when deleting the items, the list is deleted from the highest number back to the lowest number so the list stays in sequence.