Una forma rápida de devolver la lista sin un elemento específico en Python

5 minutos de lectura

avatar de usuario de fox
zorro

Si tengo una lista de palos de cartas en orden arbitrario así:

suits = ["h", "c", "d", "s"]

y quiero devolver una lista sin el 'c'

noclubs = ["h", "d", "s"]

¿Hay una manera simple de hacer esto?

  • ¿Hay alguna manera de hacer esto sin cambiar la lista original?

    – Ashe Ketchum

    10 de marzo de 2017 a las 18:21

  • @AsheKetchum Desafortunadamente no, eventualmente tendrá que hacer una copia de la lista.

    – ahora buey

    28/10/2018 a las 10:50

suits = ["h","c", "d", "s"]

noclubs = [x for x in suits if x != "c"]

  • No puedo mentir… Esperaba que hubiera algo sin bucle. Esto es mucho más corto e intuitivo en lenguajes como R.

    – tumultous_rooster

    17 de diciembre de 2013 a las 9:57

  • Evitaría esta solución ya que me parece que no está optimizada. Usar .remove() es en mi humilde opinión mucho más rápido.

    – Visgean Skeloru

    16/03/2014 a las 23:39


  • @VisgeanSkeloru No estaría de acuerdo con eso .remove() es “mucho más rápido”. Publiqué una respuesta a continuación para abordar eso (es difícil formatear el código en el cuadro de comentarios).

    – alukach

    11/11/2014 a las 22:33

  • Sí, tienes razón, mi error, no me tomé el tiempo necesario para copiar la lista en consideración… Solo estaba pensando en eliminar un elemento de una lista, no en la creación de la nueva lista… También jugué con módulo de copia y de hecho parece que [:] es la forma más rápida de copiar…

    – Visgean Skeloru

    18/11/2014 a las 21:40

  • Un bucle siempre se utilizará de alguna forma, por ejemplo. remove utiliza un bucle aunque en c nivel.

    – Jamylak

    12 de octubre de 2016 a las 4:25

avatar de usuario de jamylak
jamilak

>>> suits = ["h","c", "d", "s"]
>>> noclubs = list(suits)
>>> noclubs.remove("c")
>>> noclubs
['h', 'd', 's']

Si no necesita un separado noclubs

>>> suits = ["h","c", "d", "s"]
>>> suits.remove("c")

  • Es importante señalar aquí que list.remove(x) elimina solo el primer elemento de la lista cuyo valor es igual a x. levanta un ValueError si no hay tal artículo. El método de comprensión de listas elimina todas las instancias de x y no generará un error si el valor no existe.

    – paul

    15 de mayo de 2019 a las 0:02


avatar de usuario de alukach
alukach

Esta pregunta ha sido respondida, pero quería abordar el comentario de que usar la comprensión de listas es mucho más lento que usar .remove().

Algunos perfiles de mi máquina (computadora portátil utilizando Python 3.6.9).

x = ['a', 'b', 'c', 'd']

%%timeit
y = x[:]  # fastest way to copy
y.remove('c')

1000000 loops, best of 3: 203 ns per loop

%%timeit
y = list(x)  # not as fast copy
y.remove('c')

1000000 loops, best of 3: 274 ns per loop

%%timeit
y = [n for n in x if n != 'c']  # list comprehension

1000000 loops, best of 3: 362 ns per loop

%%timeit
i = x.index('c')
y = x[:i] + x[i + 1:]

1000000 loops, best of 3: 375 ns per loop

Si usa la forma más rápida de copiar una lista (que no es muy legible), será aproximadamente un 45% más rápido que usar la comprensión de listas. Pero si copia la lista usando el list() class (que es mucho más común y Pythonic), entonces será un 25% más lento que usar la comprensión de listas.

La verdad es que todo es bastante rápido. Creo que se podría argumentar que .remove() es más legible que list una técnica de comprensión de lista, pero no es necesariamente más rápida a menos que esté interesado en renunciar a la legibilidad en la duplicación.

La gran ventaja de la comprensión de listas en este escenario es que es mucho más sucinta (es decir, si tuviera una función que fuera eliminar un elemento de una lista determinada por algún motivo, podría hacerlo en 1 línea, mientras que el otro método requeriría 3 líneas.) Hay momentos en los que las frases de una sola línea pueden ser muy útiles (aunque normalmente tienen el costo de cierta legibilidad). Además, el uso de la comprensión de listas es excelente en el caso de que no sepa si el elemento que se va a eliminar está realmente en la lista para empezar. Tiempo .remove() lanzará un ValueErrorla comprensión de listas funcionará como se esperaba.

  • Además, tenga en cuenta que la solución de comprensión de listas eliminará todos los caracteres “c'”, mientras que remove() eliminará solo el primero.

    – Kresimir

    18 de agosto de 2017 a las 8:51

Avatar de usuario de Muayyad Alsadi
Muayyad Alsadi

puede usar filtro (o filtro de itertools)

suits = ["h","c", "d", "s"]
noclubs = filter(lambda i: i!='c', suits)

también puede filtrar usando la construcción de lista

suits = ["h","c", "d", "s"]
noclubs = [ i for i in suits if i!='c' ]

Si orden no materia, se puede utilizar una operación de conjunto:

suits = ["h", "c", "d", "s"]
noclubs = list(set(suits) - set(["c"]))
# note no order guarantee, the following is the result here:
# noclubs -> ['h', 's', 'd']

Avatar de usuario de MSeifert
MSeifert

Sin usar bucles for o funciones lambda y conservando el orden:

suits = ["h","c", "d", "s"]
noclubs = suits[:suits.index("c")]+suits[suits.index("c")+1:]

Soy consciente de que internamente seguirá usando bucles, pero al menos no tienes que usarlos externamente.

Avatar de usuario de Mark
Marca

Desafortunadamente, no parece haber nada como esto integrado en Python de forma predeterminada.

Hay varias respuestas, pero pensé que agregaría una usando iteradores. Si cambiar en el lugar es aceptable, eso será lo más rápido. Si no desea cambiar el original y solo desea recorrer un conjunto filtrado, esto debería ser bastante rápido:

Implementación:

def without(iterable, remove_indices):
    """
    Returns an iterable for a collection or iterable, which returns all items except the specified indices.
    """
    if not hasattr(remove_indices, '__iter__'):
        remove_indices = {remove_indices}
    else:
        remove_indices = set(remove_indices)
    for k, item in enumerate(iterable):
        if k in remove_indices:
            continue
        yield item

Uso:

li = list(range(5))
without(li, 3)             
# <generator object without at 0x7f6343b7c150>
list(without(li, (0, 2)))  
# [1, 3, 4]
list(without(li, 3))       
# [0, 1, 2, 4]

Así que es un generador – tendrás que llamar list o algo para hacerlo permanente.

Si solo desea eliminar un solo índice, por supuesto puede hacerlo aún más rápido usando k == remove_index en lugar de un conjunto.

¿Ha sido útil esta solución?