Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best method for changing a list while iterating over it [duplicate]

I have several instances in a python script (v2.6) where I need to modify a list in-place. I need to pop values from the list in response to interactive input from the user and would like to know the cleanest method of doing this. Currently I have the very dirty solutions of a) setting items in the list that I want to remove to False and removing them with a filter or list comprehension or b) generating an entirely new list while going through the loop, which seems to be needlessly adding variables to the namespace and taking up memory.

An example of this problem is as follows:

for i, folder in enumerate(to_run_folders):
    if get_size(folder) < byte_threshold:
        ans = raw_input(('The folder {0}/ is less than {1}MB.' + \
                    ' Would you like to exclude it from' + \
                    ' compression? ').format(folder, megabyte_threshold))
        if 'y' in ans.strip().lower():
            to_run_folders.pop(i)

I would like to look at each folder in the list. If the current folder is less than a certain size, I want to ask the user if they want to exclude it. If they do, pop the folder from the list.

The problem with this routine is that if I iterate over the list, I get unexpected behavior and early termination. If I iterate over a copy by slicing, pop doesn't pull the right value because the indices are shifted and the problem compounds as more items are popped. I have a need for dynamic list adjustment of this kind in other areas of my script as well. Is there any clean method for this kind of functionality?

like image 355
Patrick Avatar asked Apr 24 '12 20:04

Patrick


People also ask

Can I modify a list while iterating?

The list. copy method returns a shallow copy of the object on which the method was called. This is necessary because we aren't allowed to modify a list's contents while iterating over it. However, we can iterate over a copy of the list and modify the contents of the original list.

Which type of loop is most effective to iterate over a list?

While loop It works on any Collection ( List or Set ).

Can you modify a list while in a for loop?

You can't use for-in loop to modify a list because the iteration variable, ( item in your example), is only holding the value from your list and not directly pointing to that particular list item. So, you can modify item in any way you like without affecting the list.

How do you iterate over two or more lists at the same time?

Iterate over multiple lists at a time We can iterate over lists simultaneously in ways: zip() : In Python 3, zip returns an iterator. zip() function stops when anyone of the list of all the lists gets exhausted. In simple words, it runs till the smallest of all the lists.


2 Answers

You can loop over the list backwards, or use a view object.

See https://stackoverflow.com/a/181062/711085 for how to loop over a list backwards. Basically use reversed(yourList) (which happens creates a view object which visits backwards).

If you require indexing, you could do reversed(enumerate(yourList)), but that would effectively create a temporary list in memory because enumerate would need to run before reversed could kick in. You will need to either do index manipulation, or to do this:

for i in xrange(len(yourList)-1, -1, -1):
    item = yourList[i]
    ...

Even cleaner: reversed is aware of range, so you can do this in python3, or in python2 if you use xrange instead:

for i in reversed(range(len(yourList))):  
    item = yourList[i]
    ...

(proof: you can do next(reversed(range(10**10))), but this will crash your computer if using python2)

like image 108
ninjagecko Avatar answered Oct 26 '22 11:10

ninjagecko


You can loop over it backwards

Backwards:

x = range(10)
l = len(x)-1  # max index

for i, v in enumerate(reversed(x)):
    if v % 2:
        x.pop(l-i)  # l-1 is the forward index
like image 38
jdi Avatar answered Oct 26 '22 13:10

jdi