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.
List comp:
results = [x for x in (do_action(element) for element in somelist) if check(element)]
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
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.
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
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
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)
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
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)
Source: Stackoverflow.com