214

Say I have this list here:

list = [a, b, c, d, e, f, g]

How would I delete say indexes 2, 3, 4, and 5 at the same time?

pop doesn't accept multiple values. How else do I do this?

one noa
  • 345
  • 1
  • 3
  • 10
user1487497
  • 2,163
  • 2
  • 13
  • 4

8 Answers8

399

You need to do this in a loop, there is no built-in operation to remove a number of indexes at once.

Your example is actually a contiguous sequence of indexes, so you can do this:

del my_list[2:6]

which removes the slice starting at 2 and ending just before 6.

It isn't clear from your question whether in general you need to remove an arbitrary collection of indexes, or if it will always be a contiguous sequence.

If you have an arbitrary collection of indexes, then:

indexes = [2, 3, 5]
for index in sorted(indexes, reverse=True):
    del my_list[index]

Note that you need to delete them in reverse order so that you don't throw off the subsequent indexes.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • 11
    +1 How would this differ if instead of `del my_list[index]` we used `my_list.pop(index)`? Would it matter? In terms of efficiency or semantics? – Levon Jul 03 '12 at 03:20
  • Please excuse my comment, I'm learning Python, but Python reindex the array while looping for delete ? Why if you loop in normal order it raise an undefined offset exception ? – kitensei Jun 20 '14 at 21:19
  • 2
    @kitensei It does reindex, but because sorts the indexes in reverse order first, we're removing from the right-hand end of the list, so we're never reindexing an element before we delete it. – Andrew Christianson Aug 12 '14 at 21:39
  • 92
    Looping over indices backwards. Genius! I was here dreaming up some ridiculous situation of decrementing indices as I went along, when this was so much more elegant! – Neil Apr 20 '15 at 22:37
  • although a thumbs-up for the reverse deletion order, i think this would be a problem with negative indexes. – Lars Jul 02 '17 at 14:12
  • 3
    @Levon I think the main difference is that [pop returns the value](https://stackoverflow.com/a/5713303/5049813) that you remove. Since we don't need the values we're deleting, we should probably use `del` in this case, though both will work. – Pro Q Jul 19 '18 at 02:51
  • I was badly needed this approach `indexes = [2, 3, 5] for index in sorted(indexes, reverse=True): del my_list[index]` Thank you so much. – Ahmad Mar 30 '22 at 05:51
  • Please note that this solution would produce incorrect results in case indices duplicating. – Kirill Fedyanin May 26 '23 at 06:48
78
remove_indices = [1,2,3]
somelist = [i for j, i in enumerate(somelist) if j not in remove_indices]

Example:

In [9]: remove_indices = [1,2,3]

In [10]: somelist = range(10)

In [11]: somelist = [i for j, i in enumerate(somelist) if j not in remove_indices]

In [12]: somelist
Out[12]: [0, 4, 5, 6, 7, 8, 9]
Igor Chubin
  • 61,765
  • 13
  • 122
  • 144
38

There wasn't much hint on performance for the different ways so I performed a test on removing 5000 items from 50000 in all 3 generally different approaches, and for me numpy was the winner (if you have elements that fit in numpy):

  • 7.5 sec for the enumerated list comprehension [4.5 sec on another PC]
  • 0.08 sec for deleting items in reverse order [0.017 (!) sec]
  • 0.009 sec for numpy.delete [0.006 sec]

Here's the code I timed (in the third function conversion from/to list may be removed if working directly on numpy arrays is ok):

import time
import numpy as np
import random

def del_list_indexes(l, id_to_del):
    somelist = [i for j, i in enumerate(l) if j not in id_to_del]
    return somelist

def del_list_inplace(l, id_to_del):
    for i in sorted(id_to_del, reverse=True):
        del(l[i])

def del_list_numpy(l, id_to_del):
    arr = np.array(l, dtype='int32')
    return list(np.delete(arr, id_to_del))

l = range(50000)
random.shuffle(l)
remove_id = random.sample(range(len(l)), 5000) # 10% ==> 5000

# ...
Andris
  • 921
  • 7
  • 14
  • 3
    `del_list_indexes` should be much faster if you convert `id_to_del` to a set first. – C. Yduqoli Dec 02 '19 at 07:40
  • 5
    @C.Yduqoli The times I get **if using a `set` for `enumerate`** are: `enumerate` = 0.0041, `del` = 0.035, `numpy` = 0.0079, which has enumerate as the fastest as expected. – c z Jan 22 '20 at 09:29
18

If you can use numpy, then you can delete multiple indices:

>>> import numpy as np
>>> a = np.arange(10)
>>> np.delete(a,(1,3,5))
array([0, 2, 4, 6, 7, 8, 9])

and if you use np.r_ you can combine slices with individual indices:

>>> np.delete(a,(np.r_[0:5,7,9]))
array([5, 6, 8])

However, the deletion is not in place, so you have to assign to it.

fraxel
  • 34,470
  • 11
  • 98
  • 102
17

If they're contiguous, you can just do

x[2:6] = []

If you want to remove noncontiguous indexes, it's a little trickier.

x = [v for i,v in enumerate(x) if i not in frozenset((2,3,4,5))] 
Levon
  • 138,105
  • 33
  • 200
  • 191
Antimony
  • 37,781
  • 10
  • 100
  • 107
  • 1
    As a note, the reason you have to do it with an enumerate is because you can't delete on something you're iterating over, that would be bad. – Trickfire Jul 03 '12 at 01:11
  • @Trick Technically, that's only true if you're using a real iterator. If you use a simple int to maintain your position in the list, there are no problems, except that the index will skip if you delete something at a position lower than the current position. – Antimony Jul 03 '12 at 01:15
  • Yea, which could skip out of bounds, generally not a good thing to do. – Trickfire Jul 03 '12 at 01:29
6
lst = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
lst = lst[0:2] + lst[6:]

This is a single step operation. It does not use a loop and therefore executes fast. It uses list slicing.

Anthony Geoghegan
  • 11,533
  • 5
  • 49
  • 56
2

another option (in place, any combination of indices):

_marker = object()

for i in indices:
    my_list[i] = _marker  # marked for deletion

obj[:] = [v for v in my_list if v is not _marker]
Lars
  • 1,869
  • 2
  • 14
  • 26
1

Old question, but I have an answer.

First, peruse the elements of the list like so:

for x in range(len(yourlist)):
    print '%s: %s' % (x, yourlist[x])

Then, call this function with a list of the indexes of elements you want to pop. It's robust enough that the order of the list doesn't matter.

def multipop(yourlist, itemstopop):
    result = []
    itemstopop.sort()
    itemstopop = itemstopop[::-1]
    for x in itemstopop:
        result.append(yourlist.pop(x))
    return result

As a bonus, result should only contain elements you wanted to remove.

In [73]: mylist = ['a','b','c','d','charles']

In [76]: for x in range(len(mylist)):

      mylist[x])

....:

0: a

1: b

2: c

3: d

4: charles

...

In [77]: multipop(mylist, [0, 2, 4])

Out[77]: ['charles', 'c', 'a']

...

In [78]: mylist

Out[78]: ['b', 'd']

Community
  • 1
  • 1
fizban
  • 53
  • 6