deleting items within a for loop - mutable indices

Discussion in 'Python' started by SnuSnu, Apr 24, 2004.

  1. SnuSnu

    SnuSnu Guest

    Okay - here's a (probably) really easy question:

    I can't do the following, so what's the best way to handle this
    case of wanting to delete within a loop?

    x = [0,1,2,3,4,5,6,7,8]
    deletion_list = [2,7,8]

    for i in deletion_list:
    del x

    This obviously doesn't work because the list keeps changing
    each time I delete something, so the indices are no longer valid
    after the first delete (so I get an IndexError on teh last delete).

    My hack is to do this first (which makes sure that the higher indices
    are deleted first), but this is not very elegant or quick:

    # BTW: It's a shame I can't do deletion_list.sort().reverse()

    Is there a simple solution?

    (It would be nice to do something like
    del x[2,7,8] # or..
    del [*deletion_list]

    SnuSnu, Apr 24, 2004
    1. Advertisements

  2. The solution is usually to either iterate over an explicit copy of the
    list, or use functional means to build the final list up from the
    original list without mutating it at all.

    In this case it's probably best to just iterate backwards over the
    indices, since you know what you're getting and can control the order in
    which you delete the items.
    This is a deliberate design decision. These methods are made to return
    None so you're explicitly aware that they mutate the object, rather than
    return a duplicate. You can always define your own functional version:

    def mySort(seq):
    result = seq[:]
    return result
    Erik Max Francis, Apr 24, 2004
    1. Advertisements

  3. SnuSnu

    Dan Bishop Guest

    for i in deletion_list:
    x = None
    x = [num for num in x if num is not None]
    Dan Bishop, Apr 24, 2004

  4. Eh, do you want to delete the elements in the deletion list from x, or
    delete the elements in x which are at the location indicated in the
    deletion list? Your example doesn't differentiate.

    Anyhow, this is advice from a newbie :) but in the first case

    x = [y for y in x if not y in deletion_list]

    works well enough.


    tmp = [x for i in range(len(x)) if not i in deletion_list]
    x = tmp

    you could speed either up for largish lists with hashing;

    dict = {}
    for d in deletion_list:
    dict[d] = 1
    tmp = [x for i in range(len(x)) if not dict.has_key(i)]
    x = tmp

    alternatively you could make it linear in time for the number of
    deletion elements, but the performance hit from creating a bunch of
    lists hurts a bit too much, unless you don't care about order. Which
    I'm guessing you do.

    James Moughan, Apr 24, 2004
  5. SnuSnu

    Roger Binns Guest

    Here is a list comprehension that makes a new list
    without the deletion_list items. It does have the virtue
    of being a one liner :)

    newx=[x for i in range(len(x)) if i not in deletion_list]

    You will need to do timings to see what method is actually
    the fastest. Don't forget to do so with list sizes the
    same as you expect to be using in your real program. Search
    archives for this group for 'timeit' to see examples of
    how to do timings.

    (I think that the size of the deletion_list as a proportion
    of the original list is what best determines the relative
    speeds of the various methods available).

    Roger Binns, Apr 24, 2004
  6. Hello,

    this should work:

    for i in deletion_list:

    Henk-Jan de Jong, Apr 24, 2004

  7. If order doesn't matter and the items are unique you may wish to use
    sets.Set from python 2.3 instead of a dict:
    .... set.discard(item)
    ....Set(['b', 42])
    Steven Rumbalski, Apr 25, 2004
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.