I’m iterating over a list of elements in Python, do some action on it, and then remove them if they meet certain criteria.
for element in somelist:
do_action(element)
if check(element):
remove_element_from_list
What should I use in place of remove_element?
I have seen similar questions asked, but notice the presence of the do_action part that is to be executed for all elements and thus eliminates the solution of using filters.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
You could always iterate over a copy of the list, leaving you free to modify the original:
for item in list(somelist): ... somelist.remove(item)
Method 2
To meet these criteria: modify original list in situ, no list copies, only one pass, works, a traditional solution is to iterate backwards:
for i in xrange(len(somelist) - 1, -1, -1):
element = somelist[i]
do_action(element)
if check(element):
del somelist[i]
Bonus: Doesn’t do len(somelist) on each iteration. Works on any version of Python (at least as far back as 1.5.2) … s/xrange/range/ for 3.X.
Update: If you want to iterate forwards, it’s possible, just trickier and uglier:
i = 0
n = len(somelist)
while i < n:
element = somelist[i]
do_action(element)
if check(element):
del somelist[i]
n = n - 1
else:
i = i + 1
Method 3
List comp:
results = [x for x in (do_action(element) for element in somelist) if check(element)]
Method 4
for element in somelist:
do_action(element)
somelist[:] = (x for x in somelist if not check(x))
If you really need to do it in one pass without copying the list
i=0
while i < len(somelist):
element = somelist[i]
do_action(element)
if check(element):
del somelist[i]
else:
i+=1
Method 5
You can still use filter, moving to an outside function the element modification (iterating just once)
def do_the_magic(x):
do_action(x)
return check(x)
# you can get a different filtered list
filter(do_the_magic,yourList)
# or have it modified in place (as suggested by Steven Rumbalski, see comment)
yourList[:] = itertools.ifilter(do_the_magic, yourList)
Method 6
Another way of doing so is:
while i<len(your_list):
if #condition :
del your_list[i]
else:
i+=1
So, you delete the elements side by side while checking
Method 7
You can make a generator that returns everything that isn’t removed:
def newlist(somelist):
for element in somelist:
do_action(element)
if not check(element):
yield element
Method 8
Why not rewrite it to be
for element in somelist:
do_action(element)
if check(element):
remove_element_from_list
See this question for how to remove from the list, though it looks like you’ve already seen that
Remove items from a list while iterating
Another option is to do this if you really want to keep this the same
newlist = []
for element in somelist:
do_action(element)
if not check(element):
newlst.append(element)
Method 9
Not exactly in-place, but some idea to do it:
a = ['a', 'b']
def inplace(a):
c = []
while len(a) > 0:
e = a.pop(0)
if e == 'b':
c.append(e)
a.extend(c)
You can extend the function to call you filter in the condition.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0