Python: eliminar diccionario de la lista

Si tengo una lista de diccionarios, diga:

[{'id': 1, 'name': 'paul'}, {'id': 2, 'name': 'john'}] 

y me gustaría eliminar el diccionario con el id de 2 (o nombre John), ¿cuál es la forma más eficiente de hacerlo mediante progtwigción (es decir, no conozco el índice de la entrada en la lista, por lo que no se puede simplemente hacer estallar).

 thelist[:] = [d for d in thelist if d.get('id') != 2] 

Edición : como algunas dudas se expresson en un comentario sobre el rendimiento de este código (algunos se basan en un malentendido de las características de rendimiento de Python, otros en el supuesto de que hay más de un dictado en la lista con un valor de 2 para la clave) id ‘), deseo ofrecer 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 el random.shuffle (necesario para garantizar que el elemento a eliminar NO SIEMPRE esté en el mismo lugar 😉 y 0.65 microsegundos para la copia inicial (lo que más le preocupa es el impacto en el rendimiento de las copias superficiales de las listas de Python) obviamente para almorzar ;-), necesario para evitar alterar la lista original en el bucle (por lo que cada tramo del bucle tiene algo que eliminar ;-).

Cuando se sabe que hay exactamente un elemento para eliminar, es posible ubicarlo y eliminarlo de manera aún más expeditiva:

 $ 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 

(use el next método .next lugar de .next si está en Python 2.6 o superior, por supuesto), pero este código se descompone si el número de dictados 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 la ordenación aleatoria porque ya hay tres dados equispaciados para eliminar, como sabemos. Y la lista comp, sin cambios, funciona 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 eliminar. Con listas más largas y más repeticiones, esto es aún más:

 $ 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 

En general, obviamente no vale la pena desplegar la sutileza de hacer y revertir la lista de índices para eliminar, en comparación con la comprensión de la lista perfectamente simple y obvia, para ganar posiblemente 100 nanosegundos en un caso pequeño, y perder 113 microsegundos en uno más grande. ;-). Evitar o criticar soluciones simples, directas y perfectamente adecuadas para el rendimiento (como las 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 progtwigción “! -)

Aquí hay una forma de hacerlo con una lista de comprensión (suponiendo que nombre a su lista ‘foo’):

 [x for x in foo if not (2 == x.get('id'))] 

Sustituya '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.

Esto no es correctamente una respuesta (ya que creo que ya tienes algo de ellos), pero … ¿has considerado tener un diccionario de : lugar de una lista de diccionarios?

 # 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 lista en promedio porque no atraviesa toda la lista si encuentra el elemento en cuestión desde el principio.

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 puedes hacer pop desde el principio, haz pop desde el final, no arruinará el bucle for.

Podrías probar 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 cree algo parecido a un índice sobre sus datos, no creo que pueda hacerlo mejor que hacer un “escaneo 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 algo más rápido.

Supongamos que su versión de Python es 3.6 o mayor, y que no necesita el elemento eliminado, sería menos costoso …

Si los diccionarios en la lista son únicos:

 for i in range(len(dicts)): if dicts[i].get('id') == 2: del dicts[i] break 

Si desea eliminar todos los elementos coincidentes:

 for i in range(len(dicts)): if dicts[i].get('id') == 2: del dicts[i] 

También puede hacer esto para asegurarse de que obtener la clave de identificación no genere errores clave independientemente de la versión de python

si dicts [i] .get (‘id’, Ninguno) == 2