[python] How to delete an item in a list if it exists?

I am getting new_tag from a form text field with self.response.get("new_tag") and selected_tags from checkbox fields with

self.response.get_all("selected_tags")

I combine them like this:

tag_string = new_tag
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)

(f1.striplist is a function that strips white spaces inside the strings in the list.)

But in the case that tag_list is empty (no new tags are entered) but there are some selected_tags, new_tag_list contains an empty string " ".

For example, from logging.info:

new_tag
selected_tags[u'Hello', u'Cool', u'Glam']
new_tag_list[u'', u'Hello', u'Cool', u'Glam']

How do I get rid of the empty string?

If there is an empty string in the list:

>>> s = [u'', u'Hello', u'Cool', u'Glam']
>>> i = s.index("")
>>> del s[i]
>>> s
[u'Hello', u'Cool', u'Glam']

But if there is no empty string:

>>> s = [u'Hello', u'Cool', u'Glam']
>>> if s.index(""):
        i = s.index("")
        del s[i]
    else:
        print "new_tag_list has no empty string"

But this gives:

Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    if new_tag_list.index(""):
        ValueError: list.index(x): x not in list

Why does this happen, and how do I work around it?

This question is related to python list

The answer is


All you have to do is this

list = ["a", "b", "c"]
    try:
        list.remove("a")
    except:
        print("meow")

but that method has an issue. You have to put something in the except place so i found this:

list = ["a", "b", "c"]
if "a" in str(list):
    list.remove("a")

Here's another one-liner approach to throw out there:

next((some_list.pop(i) for i, l in enumerate(some_list) if l == thing), None)

It doesn't create a list copy, doesn't make multiple passes through the list, doesn't require additional exception handling, and returns the matched object or None if there isn't a match. Only issue is that it makes for a long statement.

In general, when looking for a one-liner solution that doesn't throw exceptions, next() is the way to go, since it's one of the few Python functions that supports a default argument.


try:
    s.remove("")
except ValueError:
    print "new_tag_list has no empty string"

Note that this will only remove one instance of the empty string from your list (as your code would have, too). Can your list contain more than one?


Eek, don't do anything that complicated : )

Just filter() your tags. bool() returns False for empty strings, so instead of

new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)

you should write

new_tag_list = filter(bool, f1.striplist(tag_string.split(",") + selected_tags))

or better yet, put this logic inside striplist() so that it doesn't return empty strings in the first place.


If index doesn't find the searched string, it throws the ValueError you're seeing. Either catch the ValueError:

try:
    i = s.index("")
    del s[i]
except ValueError:
    print "new_tag_list has no empty string"

or use find, which returns -1 in that case.

i = s.find("")
if i >= 0:
    del s[i]
else:
    print "new_tag_list has no empty string"

Adding this answer for completeness, though it's only usable under certain conditions.

If you have very large lists, removing from the end of the list avoids CPython internals having to memmove, for situations where you can re-order the list. It gives a performance gain to remove from the end of the list, since it won't need to memmove every item after the one your removing - back one step (1).
For one-off removals the performance difference may be acceptable, but if you have a large list and need to remove many items - you will likely notice a performance hit.

Although admittedly, in these cases, doing a full list search is likely to be a performance bottleneck too, unless items are mostly at the front of the list.

This method can be used for more efficient removal,
as long as re-ordering the list is acceptable. (2)

def remove_unordered(ls, item):
    i = ls.index(item)
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()

You may want to avoid raising an error when the item isn't in the list.

def remove_unordered_test(ls, item):
    try:
        i = ls.index(item)
    except ValueError:
        return False
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()
    return True

  1. While I tested this with CPython, its quite likely most/all other Python implementations use an array to store lists internally. So unless they use a sophisticated data structure designed for efficient list re-sizing, they likely have the same performance characteristic.

A simple way to test this, compare the speed difference from removing from the front of the list with removing the last element:

python -m timeit 'a = [0] * 100000' 'while a: a.remove(0)'

With:

python -m timeit 'a = [0] * 100000' 'while a: a.pop()'

(gives an order of magnitude speed difference where the second example is faster with CPython and PyPy).

  1. In this case you might consider using a set, especially if the list isn't meant to store duplicates.
    In practice though you may need to store mutable data which can't be added to a set. Also check on btree's if the data can be ordered.