9

Just noticed that there is no function in Python to remove an item in a list by index, to be used while chaining.

For instance, I am looking for something like this:

another_list = list_of_items.remove[item-index]

instead of

del list_of_items[item_index]

Since, remove(item_in_list) returns the list after removing the item_in_list; I wonder why a similar function for index is left out. It seems very obvious and trivial to have been included, feels there is a reason to skip it.

Any thoughts on why such a function is unavailable?

----- EDIT -------

list_of_items.pop(item_at_index) is not suitable as it doesn't return the list without the specific item to remove, hence can't be used to chain. (As per the Docs: L.pop([index]) -> item -- remove and return item at index)

Jikku Jose
  • 18,306
  • 11
  • 41
  • 61
  • 3
    Python in-place operations, as a rule, return `None`, never the altered object. Why do you expect to be able to chain when standard Python mutable types don't offer this anywhere else? – Martijn Pieters Aug 04 '13 at 14:43
  • I expected it as `remove()` exists. It returns the altered object. – Jikku Jose Aug 04 '13 at 14:53
  • 2
    Nope, `list.remove()` returns `None`, not the altered sequence. See http://ideone.com/mEH1LL – Martijn Pieters Aug 04 '13 at 15:39
  • Oh yes! That was a mistake. Apologies. – Jikku Jose Aug 06 '13 at 05:36
  • Don't know why this was downvoted. It's a perfectly valid question, and has an answer [here](http://stackoverflow.com/questions/9983254/python-list-functions-not-returning-new-lists) (the answer is: use list comprehensions) – LondonRob Aug 19 '14 at 16:00

4 Answers4

2

Use list.pop:

>>> a = [1,2,3,4]
>>> a.pop(2)
3
>>> a
[1, 2, 4]

According to the documentation:

s.pop([i])

same as x = s[i]; del s[i]; return x

UPDATE

For chaining, you can use following trick. (using temporary sequence that contains the original list):

>>> a = [1,2,3,4]
>>> [a.pop(2), a][1] # Remove the 3rd element of a and 'return' a
[1, 2, 4]
>>> a # Notice that a is changed
[1, 2, 4]
LondonRob
  • 73,083
  • 37
  • 144
  • 201
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • 1
    -1 As @BartoszKP says, `pop` can't be used for chaining. (Which is what the OP wants, despite the confusing title to the question.) – LondonRob Aug 19 '14 at 17:10
  • @LondonRob, Thank you for letting me know the reason. I updated the answer that allows chaining. – falsetru Aug 19 '14 at 17:14
  • @falsetru downvote removed. I'll add some explanation to your answer, which is pretty nice. (Although it changes `a`) – LondonRob Aug 19 '14 at 17:20
2

Here's a nice Pythonic way to do it using list comprehensions and enumerate (note that enumerate is zero-indexed):

>>> y = [3,4,5,6]
>>> [x for i, x in enumerate(y) if i != 1] # remove the second element
[3, 5, 6]

The advantage of this approach is that you can do several things at once:

>>> # remove the first and second elements
>>> [x for i, x in enumerate(y) if i != 0 and i != 1]
[5, 6]
>>> # remove the first element and all instances of 6
>>> [x for i, x in enumerate(y) if i != 0 and x != 6]
[4, 5]
Community
  • 1
  • 1
LondonRob
  • 73,083
  • 37
  • 144
  • 201
  • 1
    OP mentioned "in-place" in the title. List comprehension returns a new list. – falsetru Aug 19 '14 at 16:59
  • 3
    @falsetru Correct: the *title* mentions in-place. The *question* asks about chaining, which this will allow. In fact the questions asks for the *opposite* of in-place. `x.remove()` is in-place but doesn't allow chaining. – LondonRob Aug 19 '14 at 17:06
  • *in-place* methods can be chained. Check my updated answer. ;) – falsetru Aug 19 '14 at 17:18
1

To get the result of removing (i.e, a new list, not in-place) a single item by index, there is no reason to use enumerate or a list comprehension or any other manual, explicit iteration.

Instead, simply slice the list before and after, and put those pieces together. Thus:

def skipping(a_list, index):
    return a_list[:index] + a_list[index+1:]

Let's test it:

>>> test = list('example')
>>> skipping(test, 0)
['x', 'a', 'm', 'p', 'l', 'e']
>>> skipping(test, 4)
['e', 'x', 'a', 'm', 'l', 'e']
>>> skipping(test, 6)
['e', 'x', 'a', 'm', 'p', 'l']
>>> skipping(test, 7)
['e', 'x', 'a', 'm', 'p', 'l', 'e']
>>> test
['e', 'x', 'a', 'm', 'p', 'l', 'e']

Notice that it does not complain about an out-of-bounds index, because slicing doesn't in general; this must be detected explicitly if you want an exception to be raised. If we want negative indices to work per Python's usual indexing rules, we also have to handle them specially, or at least -1 (it is left as an exercise to understand why).

Fixing those issues:

def skipping(a_list, index):
    count = len(a_list)
    if index < 0:
        index += count
    if not 0 <= index < count:
        raise ValueError
    return a_list[:index] + a_list[index+1:]
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
-1

As Martijn Pieters noted in comments to the question, this is not implemented as: Python in-place operations, as a rule, return None, never the altered object.

Community
  • 1
  • 1
Jikku Jose
  • 18,306
  • 11
  • 41
  • 61
  • The question is why there *isn't also a not-in-place equivalent*, and how to provide one. – Karl Knechtel Sep 14 '22 at 12:05
  • Oh. I just noticed this was your own question! Well, hopefully it gives something to think about. The `skipping` function in my answer, for example, really could have been provided as a method, it just wasn't. I suppose one reason is that it's hard to make it clear from the name whether the input should be an index or a value to search for. – Karl Knechtel Sep 14 '22 at 12:17