Python idiom for iterating over changing a list

Is there a better (more obvious / idiomatic) way in python to write an equivalent

index = 0
while index < len(some_list):
    do_some_stuff(some_list[index]) # may have many side effects
    if delete_element(some_list[index]):
        del some_list[index]
        index += 1


or a dictionary equivalent to this dictionary for dictionaries? Dict / list comprehension is not a thing, because the result delete_element

can depend on do_some_stuff



source to share

4 answers

If the order doesn't matter, you can enumerate backwards so that deleting doesn't mess up a part of the list that you haven't processed yet.

for i, item in enumerate(reversed(somelist), -len(somelist)+1):
    if delete_element(item):
        del somelist[-i]


If order matters, reverse the list, do this trick, then reverse it again. This will confuse them!

Depending on the situation, you can replace the element with a marker such as None. Either cross it out later, or use other places where this list is used.

for i, item in enumerate(somelist)):
    if delete_element(item):
        somelist[i] = None

somelist = [item for item in somelist if item]




You can split the operations into two separate loops and use a list comprehension for the second part.

for value in some_list:

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 it enumerate

to keep track of the indexes without having to manually maintain the counter.

for index, value in enumerate(some_list[::-1]):
    if delete_element(value):
        del some_list[-index - 1]


You want to iterate backwards, so you don't need to tweak index

for deleted items.



you might even guess what is do_some_stuff

returning item

, do something like:

res = [item for item in some_list if not delete_element(do_some_stuff(item))]




A hopeless way is to use a generator:

def do_stuff_and_filter_items(items):
    for item in items:
        if not delete_element(item):
            yield item


Then, to get the iterable:

items = do_stuff_and_filter_items(items)


or get a list:

items = list(do_stuff_and_filter_items(items))


or overwrite the old list:

items[:] = do_stuff_and_filter_items(items)




All Articles