I'm trying to figure out how to delete an entire list at a specific index if the first element on the inside list meets a certain condition. Below I've shown an example of what I want done but when I run the code I'm getting a list index out of range error in python. If the list[i][0] meets a certain condition I want that entire list delete from the overall list.
list = [[0, 0, 0], [1, 1, 1], [2, 2, 2]]
for i in range(0, len(list)):
if list[i][0] == 0:
del list[i]
return list
Below I've shown a picture of what happens when I run the sample code in IDLE, the first time I run the loop it gives an error but the second time I run the code (copy and pasted both times) it doesn't and it does what I'm asking.
Weird Python Error
Deleting an element from the middle of a list moves everything down, breaking indexing. You need to either remove elements from the end:
for i in range(len(lst)-1, -1, -1):
if lst[i][0] == 0:
del lst[i]
or build a new list and assign it back to the variable, which would also be much more efficient:
lst = [x for x in lst if x[0] != 0]
Try changing your code to this, and 'list' is not a good variable name since it's already a builtin function:
my_list = [[0, 0, 0], [1, 1, 1], [2, 2, 2]]
my_list = [l for l in list if l[0] != 0]
print(my_list)
Usually it's not a good idea to remove elements from list while iterating through it like that because the first time you remove an element the list will no longer be the same length.
1) Create completely new list which will contain elements you want to keep:
listname = [element for element in listname if element[0] != 0]
2) Modify the list you already have (you can do this since lists are mutable):
listname[:] = [element for element in listname if element[0] != 0]
I would recommend using the second approach in case you have references to the same list somewhere else in you program.
Also try not to name your variables list, it's really not a good practice and it probably is not possible since it's keyword.
First, do not ever call your variables list.
Second, a while loop may be a slightly better solution:
stop = range(len(mylist))
i = 0
while i < stop:
if mylist[i][0] == 0:
del mylist[i]
stop -= 1
else:
i += 1
Third, a list comprehension is event better:
[item for item in mylist if item[0] != 0]
It's always bad idea to remove elements from a container while you are iterating over it.
A better approach would be to instead of removing bad elements from the list, copy good ones to a new list:
original = [[0, 0, 0], [1, 1, 1], [2, 2, 2]]
new_list = []
for l in original:
if list[0] != 0:
new_list.append(l)
return new_list
And by the way, "list" is a python keyword and can't be used as variable name.
Or better, use the built-in filter function:
return filter(lambda l : l[0] != 0, original)
Related
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 3 years ago.
I have a list in python and every time an element satisfies a certain condition I remove the element. The problem is that the for cycle seems to be skipping some elements. I think it's because the list is moved to the left after a delete. So how to properly remove items in a list? This is my code
list = [0, 0, 0, 1, 1, 0 ,0, 1, 0]
for elem in list:
if elem == 0:
list.remove(elem)
print(list)
and this is the result [1, 1, 0, 1, 0]
You shouldn't delete or add elements to a list whilst iterating over it. Here are some alternatives.
Method 1: Create a new filtered list.
[x for x in my_list if x != 0]
Method 2: If the items are booleans, you can use filter.
list(filter(lambda x: x, my_list)) # or lambda x: x != 0 depending on intent.
Method 3: Create an index of items to be deleted and then subsequently remove them.
idx = [n for n, x in enumerate(my_list) if x == 0]
for i in reversed(idx):
my_list.pop(i)
Method 4: Per the solution of Alex Martelli in the related question
my_list[:] = [x for x in my_list if x != 0]
This assigns to a list slice that matches the my_list variable name, thereby preserving any existing references to this variable.
You can't use .remove() because that removes the first occurrence of the given value, but you need to remove items by their position in the list.
Also you'll want to iterate the positions in reverse order, so removing an item doesn't shift the positions of the remaining (earlier) list items.
for position in reversed(range(len(mylist))):
if mylist[position] == 0:
del mylist[position]
use filter
lst = [0, 0, 0, 1, 1, 0 ,0, 1, 0]
lst = list(filter(lambda elem: elem != 0, lst))
print(lst)
also avoid defining the term list as this is already being used by the built in list type, otherwise you'll prevent yourself from using terms like list(...) inside of this scope.
alternatively you can use a list comprehension
lst = [0, 0, 0, 1, 1, 0 ,0, 1, 0]
lst = [elem for elem in lst if elem != 0]
print(lst)
There are more 'pythonic' ways to solve this problem using a lambda function. But to solve this using just looping it becomes easier to understand:
list = [0, 0, 0, 1, 1, 0 ,0, 1, 0]
i = 0
length = len(list)
while (i < len(list)):
if list[i] == 0:
list.remove(list[i])
i -= 1
length -= 1
else:
i += 1
print(list)
By decreasing i by 1 inside the if statement we can ensure we don't miss any elements. Also to prevent out of bounds looping we must also decrease the length used in the while condition.
You can use this if you always want to remove 0's from your list:
[elem for elem in list if elem]
This obviously gives you a new list without making changes to your existing list.
As you said, when the item is deleted, the list moves. Since the for loop uses the index within the loop, it gets the index wrong and the wrong item gets removed.
A simple way around this, if you don't wish to rewrite a lot of code is to traverse the list backwards
list = [0, 0, 0, 1, 1, 0 ,0, 1, 0]
for elem in reversed(list):
if elem == 0:
list.remove(elem)
print(list)
I am running Python3.6 and am working with lists which contain other lists within it.
list_array = [[1,0,1,0,2,2],
[1,1,2,0,1,2],
[2,2,2,1,0,1]]
I would like to modify the list called list_array be deleting all the entries with value 2 within the sub lists.
The code I used for this is
for k in list_array:
k = [x for x in k if x!=2]
However, this code doesn't modify list_array.
Why isn't it possible to replace the elements in the lists within list_array this way?
You are creating a new list instead of assigning to the old one. You can fix this by adding an assignment using k[:] =, like this:
for k in list_array:
k[:] = [x for x in k if x!=2]
Your code creates a new list every time, and erase the previous one.
At the last iteration you should get this:
k = [1, 0, 1]
Instead, a list comprehension works fine:
list_array = [[x for x in sublist if x != 2] for sublist in list_array]
Output:
[[1, 0, 1, 0], [1, 1, 0, 1], [1, 0, 1]]
If you want to write it with an explicit for loop, it could be done like this:
new_list_array = list()
for sublist in list_array:
new_list_array.append([x for x in sublist if x != 2])
You are not modifying the old list you can modify it like that
list_array = [[1,0,1,0,2,2],[1,1,2,0,1,2],[2,2,2,1,0,1]]
for k in list_array:
k[:]= [x for x in k if x!=2]
print(list_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]
This is my code:
print('oc before remove',oc)
print('remlist before remove', remlist)
for i in range(len(remlist)):
for j in range(len(oc)):
if ( (remlist[i][0] == oc[j][0]) and (remlist[i][1] == oc[j][1]) ):
del oc[j]
print('oc after remove', oc)
'oc' is de list from which I want to remove items that also occur in 'remlist'. My prints output the following:
('oc before remove', [[0, 0, 0]])
('remlist before remove', [[0, 0, 0]])
('oc after remove', [])
('oc before remove', [[1, 0, 1], [0, 1, 1]])
('remlist before remove', [[0, 0, 0], [1, 0, 1]])
Here the error occurs.
So the first time it succeeds, but the second time the following error is given:
IndexError: list index out of range
I understand the meaning of this error, but I don't see why this error occurs here. I use the length of both lists to loop. What is going wrong here?
Your problem is that you change the size of the list during iteration. Which obviously is a problem since after deleting a few items your j loop variable is going to be outside the range of the new (after deletion) list length. The first time it only works because the list only contains 1 element.
Try this instead:
oc = [item for item in oc if item not in remlist]
This list comprehension will keep the items from oc that are not in remlist.
As you have deleted one element from oc during the runtime thats why it will give IndexError: list index out of range, oc[1] element will be missing.
I handel the case using while loop.
>>>oc = [[1, 0, 1], [0, 1, 1]]
>>>remlist = [[0, 0, 0], [1, 0, 1]]
for i in range(len(remlist)):
j = 0
while j <len(oc):
if ( (remlist[i][0] == oc[j][0]) and (remlist[i][1] == oc[j][1]) ):
del oc[j]
j = j-1
j = j+1
Result :
>>>oc
[[0, 1, 1]]
>>>remlist
[[0, 0, 0], [1, 0, 1]]
len(oc) is only evaluated once when you enter the loop, but after one iteration you remove an element and therefor the length of the list is changed. On the next iteration you are trying to access oc[1][0], but at this point oc only has 1 element and therefor throws an exception.
Also note that you only compare the first 2 elements in each element (which in your example contain 3 elements each).
The problem is that you delete items from a list while you are looping over it.
For lists of length 1 this will not give any problem, but anything longer than 1 will give problems, since the list becomes shorter while looping over it.
The thing is, in your second example, that beforehand you tell the loop to go over 2 items (since your list is of length 2). But if you find and remove an item, the list becomes smaller, and it will not be able to loop the full range that you set beforehand. I has become a list of length 1, and therefore you cannot access the second item.
There are two ways:
1) Creating a new list and copying in it the elements that are in oc and not in remlist,
2) Deleting the elements that are in remlist directly from oc (in the case oc is big).
Copying into a new list
res = []
for e in oc:
if not e in remlist:
res.append(e)
Deleting directly from the list
Here, you can use remove.
for e in remlist:
for i in xrange(oc.count(e)): oc.remove(e)
Remark
I do not know why do you compare only the first and second elements of the sub-lists:
if ((remlist[i][0] == oc[j][0]) and (remlist[i][1] == oc[j][1])): ...
It is enough to write:
if (remlist[i] == oc[j]): ...
If you are sure of what you are doing, use at least:
if (remlist[i][0:2] == oc[j][0:2]): ...
It is more pythonic ;)
I'd make a function in python, that given a list returns a list of list, in which every element is the list given decreased by one.
Input: list_decreaser([0,3,4,5,6,7,8)
Output: [[0,3,4,5,6,7],[0,3,4,5,6],[0,3,4,5],[0,3,4],[0,3],[0]]
My attempt:
def list_decreaser(list):
listresult = []
for x in range(len(list)-1):
list.remove(list[x])
listresult.append(list)
return listresult
The code appends the same list multiple times. It should append copy of the list.
And use del list[..] instead of list.remove(list[..]) to delete an item at specific index.
def list_decreaser(xs):
listresult = []
for i in range(len(xs)-1, 0, -1): # <--- interate backward
del xs[i]
listresult.append(xs[:]) # <----
return listresult
print(list_decreaser([0,3,4,5,6,7,8]))
Or using list comprehension:
>>> xs = [0,3,4,5,6,7,8]
>>> [xs[:i] for i in range(len(xs)-1, 0, -1)]
[[0, 3, 4, 5, 6, 7], [0, 3, 4, 5, 6], [0, 3, 4, 5], [0, 3, 4], [0, 3], [0]]
BTW, don't use list as a variable name. It shadows builtin list function.
The problem is that you're appending the same list over and over again. You keep mutating the list in-place, but you're never creating a new list. So you end up with a list of N references to the same empty list.
This is the same problem discussed in two FAQ questions. I think How do I create a multidimensional list explains it best.
Anyway, what you need to do is append a new list each time through the loop. There are two ways to do that.
First, you can append a copy of the current list, instead of the list itself:
def list_decreaser(list):
listresult = []
for x in range(len(list)-1):
list.remove(list[x])
listresult.append(list[:]) # this is the only change
return listresult
This solves your problem, but it leaves a few new problems:
First, list.remove(list[x]) is a very bad idea. If you give it, say, [0, 1, 2, 0], what happens when you try to remove that second 0? You're calling list.remove(0), and there's no way the list can know you wanted the second 0 rather than the first! The right thing to do is call del list[x] or list.pop(x).
But once you fix that, you're removing the elements from the wrong side. x is 0, then 1, then 2, and so on. You remove element 0, then element 1 (which is the original element 2), then element 2 (which is the original element 4), and eventually get an IndexError. Even if you fixed the "skipping an index" issue (which is also explained in the FAQ somewhere), you'd still be removing the first elements rather than the last ones. You can fix that by turning the range around. However, there's an even easier way: Just remove the last element each time, instead of trying to figure out which x is the right thing, which you can do by specifying -1, or just calling pop with no argument. And then you can use a much simpler loop, too:
def list_decreaser(list):
listresult = []
while list:
list.pop()
listresult.append(list[:])
return listresult
Of course this appends the last, empty list, which you apparently didn't want. You can fix that by doing while len(list) >= 1, or putting an if list: listresult.append(list[:]), or in various other ways.
Alternatively, you can make new truncated lists instead of truncating and copying the same list over and over:
def list_decreaser(list):
listresult = []
while len(list):
list = list[:-1]
listresult.append(list)
return listresult
Note that in this second version, rather than changing the value stored in list, we're creating a new list and storing that new list in list.
use this
def list_decreaser(list1):
listresult = []
for i in list1:
list1 = list[:-1]
listresult.append(list1)
return listresult