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']
Related
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 1 year ago.
I have nested list like that [a,1],[b,2],[c,3]
My code:
for i in list:
if i[0] == 'b':
del i
And thats just not working. Code do not crash, but it does not delete neither.
I was trying something like that:
for i in list:
if something:
del i[1]
del i[0]
But it returns [a,1],[],[c,3] and i am not satisfied with that.
It is not a good idea to delete from a list while iterating over it at the same time, try using a list comprehension instead:
List = [L for L in List if L[0] != 'b']
You can use the enumerate method for that purpose. Take a look at the code snippet below.
li = ['a' , 'b' , 'c' , 'd']
for i , elm in enumerat(li):
if elm == 'a':
del li[i]
Using the enumerate function, you'll have access to two values. The first value, i in the code is the index of the element and the second value is the element itself.
You can check the elements in self in each iteration and then use the index to modify the list as needed.
The other answer is great, but to answer your question (even if it is not a good practice to delete while iterating):
list = [['a',1],['b',2],['c',3]]
for i in list:
if i[0] == 'b':
list.remove(i)
print(list)
You could also avoid deleting from the list you're iterating through like this:
list = [['a',1],['b',2],['c',3]]
for i in list.copy():
if i[0] == 'b':
list.remove(i)
print(list)
The list comprehension in the other example is cleaner and more readable, though.
sublist = [[32,999,15,329,679],[1738,100,55,1800,1469],["bruges","manchester","bristol","edinburgh","barcelona"]["Vienna","Stockholm","Berlin",Prague,"Dublin"]]
These sublists purposely have a mix of strings and integers for example but with 6 more sublists. Is there a way to search for example "Vienna" and return 4 as in "Vienna" is in the fourth sublist.
Many thanks
If you only have one level, you can just loop through this list and check whether the element is in this list, using the in keyword.
If, however, you have nested sublists with several levels, you might want to use a recursive function to scan all of them. The function finder() below scans through all nested lists and returns the indices of the lists in which the element was first encountered:
sublist = [[32,999,15,329,679],
[1738,100,55,1800,1469],
["bruges","manchester","bristol","edinburgh","barcelona"],
["Vienna","Stockholm","Berlin","Prague","Dublin"],
["Dog",["Cat","Parrot"]]]
def finder(nested,element):
for i in range(len(nested)):
if type(nested[i])==list:
f = finder(nested[i],element)
if type(f)==list:
return [i]+f
elif nested[i]==element:
return [i]
print(finder(sublist,"Vienna"))
print(finder(sublist,"Parrot"))
#Output:
#[3, 0]
#[4, 1, 1]
The output means that "Vienna" is in list 3, the 0th element. The element "Parrot" was in list 4, within list 1, the 1th element.
Here is a possible solution:
result = next((i for i, lst in enumerate(sublist) if 'Vienna' in lst), None)
In your specific case 3 is returned. If no suitable sublist is found (i.e., no sublists contains 'Vienna'), then None is returned.
You can use the enumerate function to elegantly solve your problem. However, keep in mind that Python uses 0-based indexing for arrays, so if you want to return 4 for "fourth sublist", you will need to add 1.
def find(nest, elem):
for idx, lst in enumerate(nest):
if elem in list:
return idx
sublist = [
[32,999,15,329,679],
[1738,100,55,1800,1469],
["bruges","manchester","bristol","edinburgh","barcelona"],
["Vienna","Stockholm","Berlin","Prague","Dublin"]]
print(find(sublist, "Vienna"))
To return 4:
def find(nest, elem):
for idx, lst in enumerate(nest):
if elem in list:
return idx + 1
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:
Why does this iterative list-growing code give IndexError: list assignment index out of range? How can I repeatedly add (append) elements to a list?
(9 answers)
Closed 2 years ago.
m learning python from the google tutorials. am stuck on an exercise related to lists.
getting an index error
lis[j]=words.pop()[i]
IndexError: string index out of range
i need to sort the list but the words starting with x should be the first ones.
code is
def front_x(words):
i=0
lis=[]
j=0
k=0
words.sort()
while i<len(words):
if words[i][0:1]=="x":
lis[j]=words.pop()[i]
j+=1
i+=1
lis.extend(words)
while k<len(lis):
print(lis[k])
k+=1
return
lis is an empty list, any index will raise an exception.
If you wanted to add elements to that list, use lis.append() instead.
Note that you can loop over sequences directly, there is no need to keep your own counter:
def front_x(words):
lis = []
words.sort()
for word in words:
if word.startswith("x"):
lis.append(word)
for entry in lis:
print(entry)
You can reduce this further by immediately printing all words that start with x, no need to build a separate list:
def front_x(words):
for word in sorted(words):
if word.startswith("x"):
print(word)
If you wanted to sort the list with all x words coming first, use a custom sort key:
def front_x(words):
return sorted(words, key=lambda w: (not w.startswith('x'), w))
sorts the words first by the boolean flag for .startswith('x'); False is sorted before True so we negate that test, then the words themselves.
Demo:
>>> words = ['foo', 'bar', 'xbaz', 'eggs', 'xspam', 'xham']
>>> sorted(words, key=lambda w: (not w.startswith('x'), w))
['xbaz', 'xham', 'xspam', 'bar', 'eggs', 'foo']
i need to sort the list but the words starting with x should be the first ones.
Complementary to the custom search key in #Martijn's extended answer, you could also try this, which is closer to your original approach and might be easier to understand:
def front_x(words):
has_x, hasnt = [], []
for word in sorted(words):
if word.startswith('x'):
has_x.append(word)
else:
hasnt.append(word)
return has_x + hasnt
Concerning what was wrong with your original code, there are actually three problems with the line
lis[j]=words.pop()[i]
lis[j] only works if the list already has a jth element, but as you are adding items to an initially empty list, you should use lis.append(...) instead.
You want to remove the word starting with "x" at index i from the list, but pop() will always remove the last item. pop() is for stacks; never remove items from a list while looping it with an index!
You apply the [i] operator after you've popped the item from the list, i.e., you are accessing the ith letter of the word, which may be much shorter; thus the IndexError
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