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]
    else:
        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

.

+3


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):
    do_some_stuff(item)
    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)):
    do_some_stuff(item)
    if delete_element(item):
        somelist[i] = None

somelist = [item for item in somelist if item]

      

+1


source


You can split 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 it enumerate

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



for index, value in enumerate(some_list[::-1]):
    do_some_stuff(value)
    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.

+3


source


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))]

      

0


source


A hopeless 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 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)

      

0


source







All Articles