• 10
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

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.

  • 226
Reply Report
      • 1
    • +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?
    • 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 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.
      • 2
    • 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!
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]
  • 38
Reply Report
      • 1
    • As @C.Yduqoli points out below, this is the most efficient solution (we don't need to keep rebuilding the list), but you must convert remove_indices to a set first!

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

# ...
  • 18
Reply Report
      • 1
    • @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.

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))] 
  • 13
Reply Report
      • 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.
      • 1
    • @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.

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.

  • 9
Reply Report
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.

  • 5
Reply Report
    • You should include explanation for your code. In general, describing how and why certain commands or code solves a problem is more useful as it helps the OP and others to solve similar issues.
    • wondering why 1460 days and 23410 views , did not come up with this simple solution. thats kind of depressing.
      • 2
    • I've edited your answer to improve grammar and to use lst for variable name as list is a Python data type. The reason why nobody suggested this as a solution is that this creates a new list object (based on the contents of the original) rather than actually modifying the original list object. Since the end result is similar, I'll upvote your answer. Welcome to Stack Exchange.

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]
  • 1
Reply Report

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

  • 0
Reply Report

Trending Tags