2011-08-16 15 views
29

tengo que ser capaz de encontrar un elemento en un list (un elemento en este caso ser un dict) en base a un valor dentro de ese dict. La estructura de la list necesito proceso es el siguiente:Python: obtener un diccionario de una lista basada en algo dentro de la dict

[ 
    { 
     'title': 'some value', 
     'value': 123.4, 
     'id': 'an id' 
    }, 
    { 
     'title': 'another title', 
     'value': 567.8, 
     'id': 'another id' 
    }, 
    { 
     'title': 'last title', 
     'value': 901.2, 
     'id': 'yet another id' 
    } 
] 

Advertencias:title y value puede ser cualquier valor (el mismo), id sería único.

tengo que ser capaz de obtener un dict de esta list basado en una única id. Sé que esto se puede hacer mediante el uso de bucles, pero esto parece engorroso, y tengo la sensación de que hay un método obvio de hacer esto que no veo gracias al derretimiento del cerebro.

Respuesta

54
my_item = next((item for item in my_list if item['id'] == my_unique_id), None) 

Este itera a través de la lista hasta que encuentra el primer elemento de búsqueda de my_unique_id, luego se detiene. No almacena ninguna lista intermedia en la memoria (mediante el uso de una expresión de generador) o requiere un bucle explícito. Establece my_item en None de no se encuentra ningún objeto. Es aproximadamente la misma que

for item in my_list: 
    if item['id'] == my_unique_id: 
     my_item = item 
     break 
else: 
    my_item = None 

else cláusulas sobre for bucles se utilizan cuando el bucle no se termina por una declaración break.

+1

@agf ¿Qué se recomienda cuando hay varias coincidencias y desea extraerlas en una lista (de dicts coincidentes)? – Augiwan

+1

@UGS Si necesita escanear toda la lista y crear una lista de resultados, y no solo encontrar la primera coincidencia, no puede hacer mejor que una lista de comprensión como '[item for item in my_list if item ['id'] ] == my_unique_id] '. – agf

0
In [2]: test_list 
Out[2]: 
[{'id': 'an id', 'title': 'some value', 'value': 123.40000000000001}, 
{'id': 'another id', 'title': 'another title', 'value': 567.79999999999995}, 
{'id': 'yet another id', 'title': 'last title', 'value': 901.20000000000005}] 

In [3]: [d for d in test_list if d["id"] == "an id"] 
Out[3]: [{'id': 'an id', 'title': 'some value', 'value': 123.40000000000001}] 

Uso lista por comprensión

+0

Esto sigue por la lista después de encontrar una coincidencia. – agf

+0

Si la ID debe ser única, al hacer una len() al respecto, se mostrará que obtienes identificadores no exclusivos – TyrantWave

+0

No se trata de que los identificadores no sean únicos: es la diferencia entre hacer una media de Comparaciones 'len (my_list)' o comparaciones 'len (my_list) // 2'. Su versión hace el doble de trabajo (en promedio) que sea necesario. – agf

15

Si usted tiene que hacer esto varias veces, deberá volver a crear un dictionnary indexado por id con su lista:

keys = [item['id'] for item in initial_list] 
new_dict = dict(zip(keys, initial_list)) 

>>>{ 
    'yet another id': {'id': 'yet another id', 'value': 901.20000000000005, 'title': 'last title'}, 
    'an id': {'id': 'an id', 'value': 123.40000000000001, 'title': 'some value'}, 
    'another id': {'id': 'another id', 'value': 567.79999999999995, 'title': 'another title'} 
} 

o de un modo de una sola línea como sugerido por agf:

new_dict = dict((item['id'], item) for item in initial_list) 
+2

'new_dict = dict ((item ['id'], item) para item in initial_list)' ... ¿por qué crear una lista intermedia y luego 'zip'? – agf

0

Puede crear una función simple para este propósito:

lVals = [{'title': 'some value', 'value': 123.4,'id': 'an id'}, 
{'title': 'another title', 'value': 567.8,'id': 'another id'}, 
{'title': 'last title', 'value': 901.2, 'id': 'yet another id'}] 

def get_by_id(vals, expId): return next(x for x in vals if x['id'] == expId) 

get_by_id(lVals, 'an id') 
>>> {'value': 123.4, 'title': 'some value', 'id': 'an id'} 
Cuestiones relacionadas