Is there a better (more obvious/idiomatic) way in python to write an equivalent of
index = 0
while index < len(some_list):
do_some_stuff(some_list[index])
If order doesn't matter, you can enumerate backwards so that deletes don't mess up the part of the list you haven't processed yet.
for i, item in enumerate(reversed(somelist), -len(somelist)+1):
do_some_stuff(item)
if delete_element(item):
del somelist[-i]
If order does matter, reverse the list, do this trick then reverse it again. That'll confuse 'em!
Depending on the situation, you could replace the item with a marker like None. Either strip it out later or have other places this list is used chack None.
for i, item in enumerate(somelist)):
do_some_stuff(item)
if delete_element(item):
somelist[i] = None
somelist = [item for item in somelist if item]
you could even, assuming that do_some_stuff
returns item
, do something like:
res = [item for item in some_list if not delete_element(do_some_stuff(item))]
You might break up the operations into two separate loops, and use a list comprehension for the second part.
for value in some_list:
do_some_stuff(value)
some_list = [value for value in some_list if not delete_element(value)]
Another solution would be to iterate over a copy of the list, and use enumerate
to keep track of the indices without having to maintain a counter by hand.
for index, value in enumerate(some_list[::-1]):
do_some_stuff(value)
if delete_element(value):
del some_list[-index - 1]
You'd want to iterate backwards so you don't have to adjust index
for the deleted elements.
The no-nonsense way is to use a generator:
def do_stuff_and_filter_items(items):
for item in items:
do_some_stuff(item)
if not delete_element(item):
yield item
Then to get an iterable:
items = do_stuff_and_filter_items(items)
or to get a list:
items = list(do_stuff_and_filter_items(items))
or to overwrite the old list:
items[:] = do_stuff_and_filter_items(items)