In-place function to remove an item using index in Python [duplicate]
Asked Answered
G

4

13

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)

Guaranty answered 4/8, 2013 at 14:30 Comment(5)
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?Somber
I expected it as remove() exists. It returns the altered object.Guaranty
Nope, list.remove() returns None, not the altered sequence. See ideone.com/mEH1LLSomber
Oh yes! That was a mistake. Apologies.Guaranty
Don't know why this was downvoted. It's a perfectly valid question, and has an answer here (the answer is: use list comprehensions)Simoneaux
S
3

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]
Simoneaux answered 19/8, 2014 at 16:4 Comment(3)
OP mentioned "in-place" in the title. List comprehension returns a new list.Lenalenard
@Lenalenard 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.Simoneaux
in-place methods can be chained. Check my updated answer. ;)Lenalenard
L
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]
Lenalenard answered 4/8, 2013 at 14:32 Comment(3)
-1 As @BartoszKP says, pop can't be used for chaining. (Which is what the OP wants, despite the confusing title to the question.)Simoneaux
@LondonRob, Thank you for letting me know the reason. I updated the answer that allows chaining.Lenalenard
@Lenalenard downvote removed. I'll add some explanation to your answer, which is pretty nice. (Although it changes a)Simoneaux
L
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:]
Litigate answered 14/9, 2022 at 12:14 Comment(0)
G
0

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.

Guaranty answered 6/8, 2013 at 5:44 Comment(2)
The question is why there isn't also a not-in-place equivalent, and how to provide one.Litigate
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.Litigate

© 2022 - 2024 — McMap. All rights reserved.