remove a list from a list

Steven D'Aprano steve at REMOVE.THIS.cybersource.com.au
Fri Nov 17 16:30:58 EST 2006


On Fri, 17 Nov 2006 12:00:46 -0800, Rares Vernica wrote:

> Problem context:
> 
> import os
> dirs_exclude = set(('a', 'b', 'e'))
> for root, dirs, files in os.walk('python/Lib/email'):
>      # Task:
>      # delete from "dirs" the directory names from "dirs_exclude"
>      # case-insensitive
> 
> The solution so far is:
> 
> for i in xrange(len(dirs), 0, -1):
>    if dirs[i-1].lower() in dirs_exclude:
>      del dirs[i-1]
> 
> I am looking for a nicer solution.

Define "nicer".

First thing I'd do is change the loop:

for i in xrange(len(dirs)-1, -1, -1):
    if dirs[i].lower() in dirs_exclude:
        del dirs[i]

Second thing I'd do is encapsulate it in a function instead of calling it
in place:

def remove_in_place(source, target):
    for i in xrange(len(source)-1, -1, -1):
        if source[i].lower() in target:
            del source[i]

Third thing I'd do is replace the delete-in-place code away, and build a
new list using the set idiom, finally using list slicing to change the
source in place:

def remove_in_place2(source, target):
    target = set(s.lower() for s in target)
    source[:] = [x for x in source if x.lower() not in target]
    # note the assignment to a slice

And finally, I would test the two versions remove_in_place and
remove_in_place2 to see which is faster.


import timeit

setup = """from __main__ import remove_in_place
target = list("aEIOu")
source = list("AbcdEfghIjklmnOpqrstUvwxyz")
"""

tester = """tmplist = source[:] # make a copy of the list!
remove_in_place(tmplist, target)
"""

timeit.Timer(tester, setup).timer()

You have to make a copy of the list on every iteration because you are
changing it in place; otherwise you change the values you are testing
against, and the second iteration onwards doesn't have to remove anything.


(All code above untested. Use at own risk.)

-- 
Steven.




More information about the Python-list mailing list