Why is list.remove only removing every second item? [duplicate] - python

This question already has answers here:
Strange result when removing item from a list while iterating over it
(8 answers)
Closed 7 months ago.
In my Python 2.7.2 IDLE interpreter:
>>> mylist = [1, 2, 3, 4, 5]
>>> for item in mylist:
mylist.remove(item)
>>> mylist
[2, 4]
Why?

It's because when you iterate over a list, python keeps track of the index in the list. Consider the following code instead:
for i in range(len(mylist)):
if i >= len(mylist):
break
item = mylist[i]
mylist.remove(item)
If we track this (which is essentially what python is doing in your code), then we see that when we remove an item in the list, the number to the right shifts one position to the left to fill the void left when we removed the item. The right item is now at index i and so it will never actually get seen in the iteration because the next thing that happens is we increment i for the next iteration of the for loop.
Now for something a little clever. If instead we iterate over the list backward, we'll clear out the list:
for item in reversed(mylist):
mylist.remove(item)
The reason here is that we're taking an item off the end of the list at each iteration of the for loop. Since we're always taking items off the end, nothing needs to shift (assuming uniqueness in the list -- If the list isn't unique, the result is the same, but the argument gets a bit more complicated).
Of course, If you're looking to remove all the items from a list, you can do that really easily:
del mylist[:]
or even with slice assignment:
mylist[:] = []
(I mention the latter because it can be useful to replace segments of a list with other items which don't even need to be the same length).

That's because you're modifying the list while iterating over it, iterate over a shallow copy instead:
>>> mylist = [1, 2, 3, 4, 5]
>>> for item in mylist[:]: #use mylist[:] or list(mylist)
mylist.remove(item)
...
>>> mylist
[]

You are modifying your list while you are looping through it, which is very bad practice.

Problem is that you are altering the list while iterating on it. Use a list comprehension instead:
mylist = [1, 2, 3, 4, 5]
mylist = [x for x in mylist if condition(x)]

>>> mylist = [1, 2, 3, 4, 5]
>>> for item in mylist:
mylist.remove(item)
in this loop :
1st time it will take 0th element (1) as an item and remove it from mylist.so after that mylist will be [2,3,4,5] but the pointer will be in the 1th element of the new mylist ([2,3,4,5]) that is 3. and it will remove 3.so 2 will not be remove from the list.
thats why after the full operation [2,4] will be left.
using for loop:-
>>>for i in range(len(mylist)):
mylist.remove(mylist[0])
i-=1
>>>mylist
[]
you can do this using while loop:
>>>mylist = [1, 2, 3, 4, 5]
>>>while (len(mylist)>0):
mylist.remove(mylist[0])
print mylist

Use a deep copy of mylist
mylist = [1, 2, 3, 4, 5]
l = [k for k in mylist] # deep copy
for i in range(len(mylist)):
mylist.remove(l[i])
print
print mylist

Related

Editing a List From Inside a For Loop

Let's say I have a list, mylist, and I define it to be [1, 2, 3, 4, 5]. How is there a simple way to double every element of the list from within a for loop?
I know you could do something like:
for i in range(len(mylist)):
mylist[i] *= 2
I also know that you could make a list comprehension:
mylist = [2*i for i in range(1, 6)
But is there a way to do it like this?
for num in mylist:
#code goes here
I tried doing something like:
for num in mylist:
num *= 2
but num is a local variable, so this doesn't modify mylist, just num. Does anyone know a good way to do this?
The usual way to do this is with a list comprehension, iterating directly over the list items, not indirectly via an index.
mylist = [1, 2, 3, 4, 5]
mylist = [2 * num for num in mylist]
print(mylist)
output
[2, 4, 6, 8, 10]
That replaces mylist with a new list object. That's generally ok, but sometimes you want to modify the existing object, eg when other objects have a reference to mylist. You can do that with a slice assignment.
mylist[:] = [2 * num for num in mylist]
It's not really possible since you shouldn't try to change a list while looping through it in that way. I would stick with your other two ways of manipulating the list
Using an explicit for loop. Use enumerate while iterating and assign a new value to each item.
mylist = [1, 2, 3, 4, 5]
for i, n in enumerate(mylist):
mylist[i] = n * 2
Just be careful - don't change the number of items in a list: Remove items from a list while iterating

Using for loop in Python 3.4 to remove particular element from array

As I am new to programming in Python. I am trying to remove particular elements from array using for loop which looks like
a=[2,3,1,4,1,1,1,5]
n=a.count(1)
for i in range (len(a)-n):
if (a[i]==1):
del a[i]
else:
a[i]=a[i]
print (a)
I want to remove 1 from array a. But, I am getting result as:
[2, 3, 4, 1, 1, 5].
That is 1 still exists in my new array. Can somebody please answer my problem?
try like this:
a = [2,3,1,4,1,1,1,5]
a = [x for x in a if x!=1] # this is called list comprehension
note Never modify list while iterating
Use a while loop and the remove method:
a = [2, 3, 1, 4, 1, 1, 1, 5]
while 1 in a:
a.remove(1)
print a
The real answer to your question (which none of the other answers addresses) is that every time you remove an item, the index i moves past it.
in your case:
a = [2,3,1,4,1,1,1,5]
after deleting the 5th item in the original list, the pointer moves to the 6th item, and the new 5th item (the second 1 in the sequence of three 1s) is skipped.
Regarding the comment never modify a list in a loop, try to implement an in-place algorithm like Fisher-Yates without modifying the list. Never say never. Know what you're doing.
The OP changes the list in-place, not creating a new list.
There are two methods, the second is safe, the first might be faster.
a = [2, 3, 1, 4, 1, 1, 1, 5]
toremove = 1
for i in range(len(a)-1, -1, -1):
if a[i] == toremove:
del a[i]
and
a = [2, 3, 1, 4, 1, 1, 1, 5]
toremove = 1
for i in range(a.count(toremove)):
a.remove(toremove)
The second removes the element however many times it exists (before the loop). Since we are not iterating on the list, it is safe to use the remove method.
Both fragments should be O(n) (but haven't done the calculations).
You can copy a and then remove but you cannot iterate over and delete elements from the same list, if your list starts with n elements python will have n pointers to each element so removing elements from the list as your are iterating over it will cause elements to be missed.python has no way of knowing you have removed elements from the list:
a = [2,3,1,4,1,1,1,5]
for ele in a[:]:
if ele == 1:
a.remove(1)
print(a)
[2, 3, 4, 5]
You can also use reversed which returns and iterator avoiding creating a whole copy of the list at once:
a = [2,3,1,4,1,1,1,5]
for ele in reversed(a):
if ele == 1:
a.remove(1)
print(a)
[2, 3, 4, 5]
Or using a list comprehension with the [:] syntax so we actually update the original object:
a[:] = (ele for ele in a if ele != 1)
All the above are linear operations using a single pass over a.
Actually as the del statement will remove elements from your list , and as the list that you bound in your loop doesn't been update after the first deleting you remove incorrect elements from your list , so if you want to use del you need to make the list name in your loop to reference to new list , that you can use a function for this aim , but as a more python way you can just use a list comprehension:
>>> a=[2,3,1,4,1,1,1,5]
>>> a=[i for i in a if i !=1]
>>> a
[2, 3, 4, 5]
Or you can use filter :
>>> a=[2,3,1,4,1,1,1,5]
>>> a=filter(lambda x: x !=1,a)
>>> a
[2, 3, 4, 5]

How to delete a fixed number of items from end of list in python

If you have a list
myList = [1,2,3,4,5,6,7,8,9,0]
Is there a pythonic way to delete a defined number of items from the end of a list.
EG (pseudocode):
removeFromend(myList, 3)
print myList
>>>[1,2,3,4,5,6,7]
You can use list slicing, which I think is the most pythonic way of doing it:
end_trimm = 3
myList = myList[:-end_trimm]
If you want to mutate the list, setting a slice to an empty list is equivalent to deleting those indices.
myList = [1,2,3,4,5,6,7,8,9,0]
myList[-3:] = []
myList
Out[16]: [1, 2, 3, 4, 5, 6, 7]
This works in cases where you can't simply rebind myList to a new list, e.g. you pass your list to a function and want that function to mutate your list.
deling the slice is the direct approach
>>> myList = [1,2,3,4,5,6,7,8,9,0]
>>> del myList[-3:]
>>> myList
[1, 2, 3, 4, 5, 6, 7]
>>>
The -3 means the slice starts 3 from the end, So the general form is del myList[-n:]

Python: identical items in list are treated as the same

I have a nested list in python. Each item the second list is also a nested list. My goal is to duplicate one list, insert it right at the same index, and then modify each one. So, example of start condition:
myList = [[first_list], [[element_1], [element_2, element_3], [duplicate_me]]]
Duplication/insertion at myList[1][2]:
myList = [[first_list], [[element_1], [element_2, element_3], [duplicate_me], [duplicate_me]]]
This all works fine. However, when I run the append code:
myList[1][2].append(new_element)
It appends the new element to both duplicates, like this:
myList = [[first_list], [[element_1], [element_2, element_3], [duplicate_me, new_element], [duplicate_me, new_element]]]
Is there something bizarre going on in the way the elements are called or indexed? I see a potential workaround (calling the item to be duplicated to a working variable, modifying it there, and then inserting it at the same point), but that seems needlessly complex.
Thanks!
myList[1][2] and myList[1][3] don't just have the same values, they are actually the same list. You are looking at the same area in memory when you read both of them. So, when you change one, you are changing the other, because both are actually the exact same thing! Instead doing what you do to duplicate the list, you should make a copy of it.
Here's an example of the problem from a python shell:
>>> mylist = [1, 2, 3]
>>> newlist = mylist
>>> newlist.append(4)
>>> newlist
[1, 2, 3, 4]
>>> mylist
[1, 2, 3, 4]
A common way to fix this is to use one of these tricks:
>>> mylist = [1, 2, 3]
>>> newlist = mylist[:] # OR :
>>> newlist = [x for x in mylist]
>>> newlist.append(4)
>>> newlist
[1, 2, 3, 4]
>>> mylist
[1, 2, 3]
The second and third lines will create a copy of the list. I tend to combine the two like this, if I need to copy a 2D list:
>>> newlist = [x[:] for x in mylist]
Use one of these to create your duplicate, and all will be well again.
You are most likely not duplicating the target list (*[duplicate_me]*), but rather simply appending a duplicated reference to the same list into myList.
You need to copy the list before adding it to myList. One simple way is to call the list constructor passing in the original [duplicate_me]

iterating through a list removing items, some items are not removed

I'm trying to transfer the contents of one list to another, but it's not working and I don't know why not. My code looks like this:
list1 = [1, 2, 3, 4, 5, 6]
list2 = []
for item in list1:
list2.append(item)
list1.remove(item)
But if I run it my output looks like this:
>>> list1
[2, 4, 6]
>>> list2
[1, 3, 5]
My question is threefold, I guess: Why is this happening, how do I make it work, and am I overlooking an incredibly simple solution like a 'move' statement or something?
The reason is that you're (appending and) removing from the first list whereby it gets smaller. So the iterator stops before the whole list could be walked through.
To achieve what you want, do this:
list1 = [1, 2, 3, 4, 5, 6]
list2 = []
# You couldn't just make 'list1_copy = list1',
# because this would just copy (share) the reference.
# (i.e. when you change list1_copy, list1 will also change)
# this will make a (new) copy of list1
# so you can happily iterate over it ( without anything getting lost :)
list1_copy = list1[:]
for item in list1_copy:
list2.append(item)
list1.remove(item)
The list1[start:end:step] is the slicing syntax: when you leave start empty it defaults to 0, when you leave end empty it is the highest possible value. So list1[:] means everything in it. (thanks to Wallacoloo)
Like some dudes said, you could also use the extend-method of the list-object to just copy the one list to another, if this was your intention. (But I choosed the way above, because this is near to your approach.)
As you are new to python, I have something for you: Dive Into Python 3 - it's free and easy. - Have fun!
You're deleting items from list1 while you're iterating over it.
That's asking for trouble.
Try this:
>>> list1 = [1,2,3,4,5,6]
>>> list2 = []
>>> list2 = list1[:] # we copy every element from list1 using a slice
>>> del list1[:] # we delete every element from list1
Essential debugging skill: Add print statements. (or print functions in Python 3)
>>> list1= [1, 2, 3, 4, 5, 6]
>>> for item in list1:
... print item
... list1.remove(item)
... print list1
...
1
[2, 3, 4, 5, 6]
3
[2, 4, 5, 6]
5
[2, 4, 6]
Notice that Python is trying to step through the positions of the list, but you keep removing items from the list, making the positions become meaningless.
Python picks the item at position 0 from the list.
You then remove the item, changing the list.
Python then picks the item at position 1 from the list (appearing to skip an item)
You then remove that item, changing the list.
Python then picks the item at position 2 from the list (appearing to skip an item)
You then remove the item, changing the list.
Python would then like to pick the item at position 3, but there's no such item. So the loop stops.
You shouldn't modify a list while you iterate over it. This causes the iterator to point at the wrong item. After the first item is handled, the iterator is pointing at index = 1, but because you've removed an item, the next item is now at index zero, so it will be skipped. This is why you are only handling every other item.
Try:
list2.extend(list1) # This appends all items from list1 to list2.
del list1[:] # From ChristopheD's post.
Exactly as ChristopheD says.
Could do this:
list1 = [1, 2, 3, 4, 5, 6]
list2 = []
for item in list1:
list2.append(item)
list1 = []
That'll clear list1.
Edit He/she has updated their post. I'll leave this up as a slight alternative.
Try this instead:
list1 = [1, 2, 3, 4, 5, 6]
list2 = []
list2.extend(list1)
list1[:] = []
There is also the humble copy function (or deepcopy if you have complex objects and not just integers in your list):
from copy import copy
list2 = copy(list1)
You might get a more appropriate answer if you explain what you're trying to accomplish (unless you're just learning about python lists).
"Variables" in Python are just names/references, so the destructive copy you seem to want to do seems kind of strange. If you want list2 to have the same values as list1 you can just do:
list2 = list1 # now they are both referring to the same list
And if after that you want to use list1 for something else, you can just do:
list1 = ['A', 'B', 'C']
Using a list comprehension:
list2 = [item for item in list1]
Bind the name list2 to the the same object as list1:
list2 = list1
(Note that if you modify the contents of list1, list2 will be change accordingly.)
Create a copy of list1 and bind it to the name list2:
list2 = list1[:]
In this case list1 and list2 are different objects.
As you can see in other answers, you are trying to modify the list while iterating over it. This doesn't work. There are many ways to copy one list to another. I did some tests to see how fast each approach is:
>>> timeit.Timer('list2 = list1[:]', 'list1 = range(10**3)').timeit(10**6)
3.9134418964385986
>>> timeit.Timer('list2 = []; list2.extend(list1)', 'list1 = range(10**3)').timeit(10**6)
4.9082601070404053
>>> timeit.Timer('list2 = copy.copy(list1)', 'import copy; list1 = range(10**3)').timeit(10**6)
7.5023419857025146
>>> timeit.Timer('list2 = [i for i in list1]', 'list1 = range(10**3)').timeit(10**6)
95.697894811630249
The slice syntax is the fastest. It's much faster than using a list comprehension.
To clear a list, you can use:
del list1[:]
#potatocubed: Many of the answers given solve the trivial example you gave ("move list1 to list2"), but don't really explain the "why" of the deeper problem, which is modifying a list as you iterate over it. Study S.Lott's answer...

Categories