This question already has answers here:
Strange result when removing item from a list while iterating over it
(8 answers)
Closed 7 years ago.
I have a list in the given format:
[['John', 'Smith'], ['Linus', 'Torvalds'], ['Bart', 'Simpson']]
There are some elements like this in the list ['Linus Torvalds', ''] and I want to remove those. So why doesn't the following code remove them?
for i in people:
if(i[0] == '' or i[1] == ''):
print people.pop(people.index(i))
You are changing the list while iterating over it and this is the source of your problems. An approach that works is
people[:] = [p for p in people if p[0] != '' and p[1] != '']
this way a new temporary list containing only the elements you want is built and then assigned to the original list object when the operation is complete.
Or even people[:] = [p for p in people if all(p)] if you want to resize the list "in place".
You're modifying the list's length while iterating over it. That causes you to skip values. When you pop one item off the list, here's what happens (stealing from this answer):
[1, 2, 3, 4, 5, 6...]
^
That's the state of the list initially; now say 1 is removed and the loop goes to the second item in the list:
[2, 3, 4, 5, 6...]
^
And so on.
It's a bad idea to remove things from a list as you iterate over it. So, try one of these instead (Also, I think your condition is not what you want it to be - I've fixed it):
L = [['John', 'Smith'], ['Linus', 'Torvalds'], ['Bart', 'Simpson']]
delete_these = []
for index, i in enumerate(L):
if not i[-1].strip():
delete_these.append(i)
for i in delete_these:
L.pop(i)
delete_these = map(lambda x: x-1, delete_these)
OR
L = [i for i in L if i[-1].strip()]
OR
answer = []
for i in L:
if i[-1].strip():
answer.append(i)
OR
i = 0
while i < len(L):
if not L[i][-1].strip():
L.pop(i)
else:
i += 1
Hope this helps
Related
This question already has answers here:
python : list index out of range error while iteratively popping elements
(12 answers)
Closed 2 years ago.
Im getting an error for list index being out of range. Sorry if this is a stupid question.
def filter_list(l):
for x in range(0, len(l)):
if type(l[x]) is str:
del l[x]
return l
When you use del to delete the item in the list, the list actually shortens and it seems like that is what is causing the problem.
If you would like to filter lists, you could use list comprehensions like so:
def filter(l):
return [item for item in l if type(item) is not str]
Usually, when looping over lists, it is good practice not to delete or insert new items.
Hope this helped
You should not definitely change a list while iterating over it. It is very bad practise... it can lead to a whole lot of errors for you. Either you should create a copy of use something else, as list comprehension:
def filter_list(l):
return [x for x in l if type(x) is not str]
print(filter_list([1, 4, 5, 's', 'demo'])) # Prints [1, 4, 5]
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 2 years ago.
Can someone explain to me why this code removes only one element from the list not each as I would expect?
So I have a list, let's say it is: [1,2,'a','b']
I want to remove each string from the list.
def filter_list(l):
for i in l:
if type(i) == str:
l.remove(i)
filter_list([1,2,'a','b'])
So after this I receive = [1,2,'b'] which is confusing.
Check this out:
l = [1,2,'a','b']
print([item for item in l if isinstance(item, int)])
I suggest you use isinstance built-in function instead of type. And also it is better to check is an integer in case of your list has an item that different from string or integer.
If you want to remove it, you can simply assign the new list on the old one.
l = [1,2,'a','b']
l = [item for item in l if isinstance(item, int)]
print(l)
Why you cannot over loop:
Let me explain.
def filter_list(l):
for index, i in enumerate(l):
if type(i) == str:
l.remove(i)
return l
filter_list([1,2,'a','b'])
1st iteration the index of for loop is 0:
i becomes 1, and no need to remove it.
2nd iteration the index of for loop is 1:
i becomes 2, and no need to remove it.
3rd iteration the index of for loop is 2:
i becomes 'a', and need to remove it.
At this point, we simply removed the 'a'. The length of the list changed.
It decreased by 1. The new list became [1, 2, 'b'] because we removed 'a'.
4th iteration the index of for loop is 3:
Since we removed the variable in the previous iteration, the list no longer has the index of 3. The loop will raise StopIteration error(IndexError), and breaks.
That is why we cannot do this properly.
Lambda Function:
[*filter(lambda x: isinstance(x, int), l)]
Data:
l = [1,2,'a','b']
This question already has an answer here:
safe usage of remove method in python
(1 answer)
Closed 4 years ago.
import itertools
Numb = 'abxxxc'
L=list(itertools.permutations(list(Numb)))
L=[''.join(str(i) for i in x) for x in L]
K=L[::]
for i in range(len(L)):
if K[i][-1] == 'x':
del K[i]
print(L)
I have a code like this and when I run this it gives me an error.
if K[i][-1]=='x':
IndexError: list index out of range
and I have no idea why. I tried everything but still it gives me error.Can someone please help me to see where is my error ?
Edit:
L=list(itertools.permutations(list(Numb)))
L=[''.join(str(i) for i in x) for x in L]
for i in range(len(L)):
if L[i][5] == 'x':
del L[i]
print(L)
L is 720 elements long.
for i in range(len(L)) corresponds to for i in range(720)
You delete elements from K, so at a certain moment, K is 432 elements long, and i is 432
(you are trying to take the 433nd element of a 432-long list)
Perhaps this should do the job ?
import itertools
Numb = 'abxxxc'
L=list(itertools.permutations(list(Numb)))
L=[''.join(str(i) for i in x) for x in L]
K = [elt for elt in L if elt[-1] != 'x']
print(K)
You should usually try to avoid using del. It is better to construct a new list containing the elements you need.
It's a bad practice to delete elements while iterating a list.
A better version will be:
K = [element for element in K if element[-1] != 'x']
You are erasing element from the list while you're iterating it, so there will be a point where you refear to an index that does not exist anymore.
You can solve this problem with list comprehension:
mylist = [K[x] for x in range(len(L)) if (K[x][-1] != 'x')]
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 6 years ago.
I have a list such as this:
l = ['(7 - 3)', '7 - 6', '(a + 13)']
I want to remove any expression that does NOT contain the letter "a" therefore the only expression that would be left is ['(a+13)'].
So far I have tried this:
for i in range(len(l)):
if "a" not in l[i]:
l.pop(i)
else:
print(l[i])
However I am getting a list index out of range error, and do not know how to fix this.
Could anyone help resolve this?
l.pop() removes elements from the list, so your list is getting smaller but you are not adjusting your iteration.
>>> x = [1,2,3]
>>> x.pop(0)
1
>>> x
[2, 3]
>>> x.pop(1)
3
>>> x
[2]
>>> x.pop(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: pop index out of range
I would suggest you take a different approach. You could iterate over the elements of the list directly and print the ones that have "a" in them.
for i in l:
if "a" in i:
print(i)
If you need the list to have only the elements that have "a" in them at the end of your iteration, perhaps you would like to add those items to a new list.
things_with_a_in_them = []
for i in l:
if "a" in i:
things_with_a_in_them.append(i)
print(i)
If you want it to be terser...
things_with_a_in_them = [i for i in l if "a" in i]
map(print, things_with_a_in_them)
Have fun playing around with different approaches.
Never structurally manipulate a list while iterating over it! Use a conditional comprehension
l = [x for x in l if 'a' in x]
or filter:
l = filter(lambda x: 'a' in x, l)
# in Python3, you will have to convert the filter object to a list
# l = list(filter(lambda x: 'a' in x, l))
The problem is len(l) returns 3, but as soon as you pop an item, your list becomes shorter. Easiest thing to do (assuming your list isn't prohibitively giant) would be to just write a new list:
l_new = [i for i in l if "a" in i]
If you don't want to create a new list, and keep your logic with the pop, you would have to check for the len(l) everytime, you can do that by:
l = ['(7 - 3)', '7 - 6', '(a + 13)']
i = 0
while i < len(l):
if "a" not in l[i]:
l.pop(i)
else:
print(l[i])
i += 1
But as others have aswered, it would be better to create a new list with list comprehension
This question already has answers here:
Elegant way to remove items from sequence in Python? [duplicate]
(14 answers)
Closed 8 years ago.
If I have a list like this:
MyList = [1,2,3,4,5,'hi', 6,7, 'hi', 8, 'hi', 9]
how can I remove all the items 'hi' from the list? I have tried the method remove() but it works only once:
MyList.remove('hi')
>>>> MyList
[1,2,3,4,5,6,7,'hi',8,'hi',9]
Yes, I could do something like this:
while 'hi' in MyList:
MyList.remove('hi')
but does anyone know any more elegant way than iterating n-times the same instruction?
while 'hi' in MyList: MyList.remove('hi') is O(N**2), better use a simple list comprehension which does the same thing in O(N) time:
MyList = [item for item in MyList if item != 'hi']
Use a list comprehension:
MyList = [v for v in MyList if v != 'hi']
This rebuilds the list to only contain values not equal to 'hi'.
list.remove() calls must move the rest of the list items forward, using a list comprehension instead is more efficient.
You can use filter function I guess.
Here is docs: filter docs
MyList = [1,2,3,4,5,'hi', 6,7, 'hi', 8, 'hi', 9]
def f(item, element="hi"):
if item == element:
return False
else:
return True
print filter(f, MyList)
>>> [1, 2, 3, 4, 5, 6, 7, 8, 9]
Thanks to Tim Pietzcker for his improvement.
The f function could be shorter
def f(item, element="hi"): return item != element
You can also use generator expression:
NewList = (x for x in MyList if x != 'hi')
Then you can iterate this as NewList.next() until it raise StopIteration.