python - dictionaries - hunspell api
Python: eliminar el diccionario de la lista (6)
Aquí hay una manera de hacerlo con una lista de comprensión (suponiendo que nombre su lista ''foo''):
[x for x in foo if not (2 == x.get(''id''))]
Sustituye ''john'' == x.get(''name'')
o lo que sea apropiado.
filter
también funciona:
foo.filter(lambda x: x.get(''id'')!=2, foo)
Y si quieres un generador puedes usar itertools:
itertools.ifilter(lambda x: x.get(''id'')!=2, foo)
Sin embargo, a partir de Python 3, el filter
devolverá un iterador de todos modos, por lo que la comprensión de la lista es realmente la mejor opción, como sugirió Alex.
Si tengo una lista de diccionarios, diga:
[{''id'': 1, ''name'': ''paul''},
{''id'': 2, ''name'': ''john''}]
y me gustaría eliminar el diccionario con id
de 2 (o nombre john), ¿cuál es la forma más eficiente de hacerlo programáticamente (es decir, no conozco el índice de la entrada en la lista para que simplemente no puede ser reventado).
Esto no es propiamente correcto (ya que creo que ya cuentas con algunos bastante buenos), pero ... ¿has considerado tener un diccionario de <id>:<name>
lugar de una lista de diccionarios?
Podría intentar algo en las siguientes líneas:
def destructively_remove_if(predicate, list):
for k in xrange(len(list)):
if predicate(list[k]):
del list[k]
break
return list
list = [
{ ''id'': 1, ''name'': ''John'' },
{ ''id'': 2, ''name'': ''Karl'' },
{ ''id'': 3, ''name'': ''Desdemona'' }
]
print "Before:", list
destructively_remove_if(lambda p: p["id"] == 2, list)
print "After:", list
A menos que construya algo similar a un índice sobre sus datos, no creo que pueda hacerlo mejor que hacer una "exploración de tabla" de fuerza bruta en toda la lista. Si sus datos están ordenados por la clave que está utilizando, es posible que pueda emplear el módulo bisect para encontrar el objeto que está buscando con mayor rapidez.
Puedes probar lo siguiente:
a = [{''id'': 1, ''name'': ''paul''},
{''id'': 2, ''name'': ''john''}]
for e in range(len(a) - 1, -1, -1):
if a[e][''id''] == 2:
a.pop(e)
Si no puede hacer pop desde el principio, pop desde el final, no arruinará el ciclo for.
# assume ls contains your list
for i in range(len(ls)):
if ls[i][''id''] == 2:
del ls[i]
break
Probablemente sea más rápido que los métodos de comprensión de listas en promedio porque no atraviesa toda la lista si encuentra el elemento en cuestión desde el principio.
thelist[:] = [d for d in thelist if d.get(''id'') != 2]
Editar : como algunas dudas se han expresado en un comentario sobre el rendimiento de este código (algunas basadas en malentender las características de rendimiento de Python, algunas sobre suponer más allá de las especificaciones dadas que hay exactamente un dict en la lista con un valor de 2 para clave '' id ''), deseo ofrecerle tranquilidad en este punto.
En una vieja caja de Linux, midiendo este código:
$ python -mtimeit -s"lod=[{''id'':i, ''name'':''nam%s''%i} for i in range(99)]; import random" "thelist=list(lod); random.shuffle(thelist); thelist[:] = [d for d in thelist if d.get(''id'') != 2]"
10000 loops, best of 3: 82.3 usec per loop
de los cuales aproximadamente 57 microsegundos para random.shuffle (necesario para asegurar que el elemento para eliminar no siempre esté en el mismo lugar ;-) y 0.65 microsegundos para la copia inicial (quien se preocupe por el impacto en el rendimiento de copias superficiales de listas de Python es obviamente, almorzar ;-), necesario para evitar alterar la lista original en el ciclo (por lo que cada tramo del ciclo tiene algo que eliminar ;-).
Cuando se sabe que hay exactamente un elemento para eliminar, es posible localizarlo y eliminarlo aún más rápidamente:
$ python -mtimeit -s"lod=[{''id'':i, ''name'':''nam%s''%i} for i in range(99)]; import random" "thelist=list(lod); random.shuffle(thelist); where=(i for i,d in enumerate(thelist) if d.get(''id'')==2).next(); del thelist[where]"
10000 loops, best of 3: 72.8 usec per loop
(utilice el next
método .next
lugar del método .next
si está en Python 2.6 o superior, por supuesto), pero este código se rompe si la cantidad de mensajes que satisfacen la condición de eliminación no es exactamente uno. Generalizando esto, tenemos:
$ python -mtimeit -s"lod=[{''id'':i, ''name'':''nam%s''%i} for i in range(33)]*3; import random" "thelist=list(lod); where=[i for i,d in enumerate(thelist) if d.get(''id'')==2]; where.reverse()" "for i in where: del thelist[i]"
10000 loops, best of 3: 23.7 usec per loop
donde se puede eliminar el barajado porque ya hay tres dicts equiespaciados para eliminar, como sabemos. Y el listcomp, sin cambios, le va bien:
$ python -mtimeit -s"lod=[{''id'':i, ''name'':''nam%s''%i} for i in range(33)]*3; import random" "thelist=list(lod); thelist[:] = [d for d in thelist if d.get(''id'') != 2]"
10000 loops, best of 3: 23.8 usec per loop
totalmente cuello y cuello, con solo 3 elementos de 99 para quitar. Con listas más largas y más repeticiones, esto tiene aún más por supuesto:
$ python -mtimeit -s"lod=[{''id'':i, ''name'':''nam%s''%i} for i in range(33)]*133; import random" "thelist=list(lod); where=[i for i,d in enumerate(thelist) if d.get(''id'')==2]; where.reverse()" "for i in where: del thelist[i]"
1000 loops, best of 3: 1.11 msec per loop
$ python -mtimeit -s"lod=[{''id'':i, ''name'':''nam%s''%i} for i in range(33)]*133; import random" "thelist=list(lod); thelist[:] = [d for d in thelist if d.get(''id'') != 2]"
1000 loops, best of 3: 998 usec per loop
Con todo, obviamente no vale la pena desplegar la sutileza de hacer y revertir la lista de índices para eliminar, frente a la comprensión de listas perfectamente simple y obvia, para posiblemente ganar 100 nanosegundos en un caso pequeño, y perder 113 microsegundos en uno más grande. ;-). Evitar o criticar soluciones simples, sencillas y perfectamente adecuadas para el desempeño (como listas de comprensión para esta clase general de problemas de "eliminar algunos elementos de una lista") es un ejemplo particularmente desagradable de la conocida tesis de Knuth y Hoare de que "la optimización prematura es la raíz de todo mal en la programación "! -)