Buscar una lista de diccionarios en Python

7 minutos de lectura

Avatar de usuario de Hellnar
Hellnar

Dado:

[
  {"name": "Tom", "age": 10},
  {"name": "Mark", "age": 5},
  {"name": "Pam", "age": 7}
]

¿Cómo busco por name == "Pam" para recuperar el diccionario correspondiente a continuación?

{"name": "Pam", "age": 7}

Avatar de usuario de Frédéric Hamidi
Frederic Hamidi

Puedes usar un generador de expresiones:

>>> dicts = [
...     { "name": "Tom", "age": 10 },
...     { "name": "Mark", "age": 5 },
...     { "name": "Pam", "age": 7 },
...     { "name": "Dick", "age": 12 }
... ]

>>> next(item for item in dicts if item["name"] == "Pam")
{'age': 7, 'name': 'Pam'}

Si necesita manejar el elemento que no está allí, entonces puede hacer lo que sugirió el usuario Matt en su comentario y proporcionar un valor predeterminado usando una API ligeramente diferente:

next((item for item in dicts if item["name"] == "Pam"), None)

Y para encontrar el índice del elemento, en lugar del elemento en sí, puede enumerar() la lista:

next((i for i, item in enumerate(dicts) if item["name"] == "Pam"), None)

  • Solo para ahorrarle un poco de tiempo a alguien más, si necesita un valor predeterminado en el evento “Pam” simplemente no está en la lista: siguiente ((elemento para elemento en dicts si elemento[“name”] == “Pam”), Ninguno)

    – Mate

    27 de noviembre de 2012 a las 22:08


  • Qué pasa [item for item in dicts if item["name"] == "Pam"][0]?

    – Moberg

    09/10/2014 a las 11:30

  • @Moberg, sigue siendo una lista de comprensión, por lo que iterará sobre toda la secuencia de entrada independientemente de la posición del elemento coincidente.

    – Frederic Hamidi

    9 oct 2014 a las 12:02

  • Esto generará un error de iteración si la clave no está presente en el diccionario

    – Kishan Mehta

    5 de junio de 2018 a las 12:25

  • @Siemkowski: luego agrega enumerate() para generar un índice en ejecución: next(i for i, item in enumerate(dicts) if item["name"] == "Pam").

    – Martijn Pieters

    22 de noviembre de 2018 a las 12:56

Avatar de usuario de PaoloC
paoloc

Esto me parece la forma más pitónica:

people = [
{'name': "Tom", 'age': 10},
{'name': "Mark", 'age': 5},
{'name': "Pam", 'age': 7}
]

filter(lambda person: person['name'] == 'Pam', people)

resultado (devuelto como una lista en Python 2):

[{'age': 7, 'name': 'Pam'}]

Nota: en Python 3, se devuelve un objeto de filtro. Entonces la solución de python3 sería:

list(filter(lambda person: person['name'] == 'Pam', people))

  • Vale la pena señalar que esta respuesta devuelve una lista con todas las coincidencias para ‘Pam’ en personas; alternativamente, podríamos obtener una lista de todas las personas que no son ‘Pam’ cambiando el operador de comparación a !=. +1

    – Onema

    12 de noviembre de 2015 a las 22:25

  • También vale la pena mencionar que el resultado es un objeto de filtro, no una lista, si desea usar cosas como len()tienes que llamar list() en el resultado primero. O: stackoverflow.com/questions/19182188/…

    – wasabigeek

    21 de diciembre de 2017 a las 16:06


  • @wasabigeek esto es lo que dice mi Python 2.7: gente = [ {‘name’: “Tom”, ‘age’: 10}, {‘name’: “Mark”, ‘age’: 5}, {‘name’: “Pam”, ‘age’: 7} ] r = filtro(lambda persona: persona[‘name’] == ‘Pam’, personas) tipo(r) lista Entonces r es un list

    – PaoloC

    26 de diciembre de 2017 a las 14:32


  • Las listas de comprensión se consideran más Pythonic que map/filter/reduce: stackoverflow.com/questions/5426754/google-python-style-guide

    – jrc

    11 de noviembre de 2019 a las 12:58

  • Consigue el primer partido: next(filter(lambda x: x['name'] == 'Pam', dicts))

    – xgMz

    12 dic 2019 a las 19:24

Avatar de usuario de Mike N
mike n

La respuesta de @Frédéric Hamidi es excelente. En Python 3.x la sintaxis para .next() cambiado ligeramente. Por lo tanto, una ligera modificación:

>>> dicts = [
     { "name": "Tom", "age": 10 },
     { "name": "Mark", "age": 5 },
     { "name": "Pam", "age": 7 },
     { "name": "Dick", "age": 12 }
 ]
>>> next(item for item in dicts if item["name"] == "Pam")
{'age': 7, 'name': 'Pam'}

Como se menciona en los comentarios de @Matt, puede agregar un valor predeterminado como tal:

>>> next((item for item in dicts if item["name"] == "Pam"), False)
{'name': 'Pam', 'age': 7}
>>> next((item for item in dicts if item["name"] == "Sam"), False)
False
>>>

  • Esta es la mejor respuesta para Python 3.x. Si necesita un elemento específico de los dictados, como la edad, puede escribir: next((item.get(‘age’) for item in dicts if item[“name”] == “Pam”), Falso)

    – csusurrador

    9 de enero de 2019 a las 7:44

Puedes usar un lista de comprensión:

def search(name, people):
    return [element for element in people if element['name'] == name]

Probé varios métodos para revisar una lista de diccionarios y devolver los diccionarios donde la clave x tiene un valor determinado.

Resultados:

  • Velocidad: comprensión de lista > expresión de generador >> iteración de lista normal >>> filtro.
  • Todas las escalas son lineales con el número de dictados en la lista (10x tamaño de lista -> 10x tiempo).
  • Las claves por diccionario no afectan significativamente la velocidad para grandes cantidades (miles) de claves. Por favor, vea este gráfico que calculé: https://imgur.com/a/quQzv (nombres de métodos ver más abajo).

Todas las pruebas hechas con Pitón 3.6.4, W7x64.

from random import randint
from timeit import timeit


list_dicts = []
for _ in range(1000):     # number of dicts in the list
    dict_tmp = {}
    for i in range(10):   # number of keys for each dict
        dict_tmp[f"key{i}"] = randint(0,50)
    list_dicts.append( dict_tmp )



def a():
    # normal iteration over all elements
    for dict_ in list_dicts:
        if dict_["key3"] == 20:
            pass

def b():
    # use 'generator'
    for dict_ in (x for x in list_dicts if x["key3"] == 20):
        pass

def c():
    # use 'list'
    for dict_ in [x for x in list_dicts if x["key3"] == 20]:
        pass

def d():
    # use 'filter'
    for dict_ in filter(lambda x: x['key3'] == 20, list_dicts):
        pass

Resultados:

1.7303 # normal list iteration 
1.3849 # generator expression 
1.3158 # list comprehension 
7.7848 # filter

  • Agregué la función z() que implementa el siguiente como lo señaló Frédéric Hamidi arriba. Aquí están los resultados del perfil de Py.

    – león

    24 de marzo de 2019 a las 1:53

  • ¿Alguien sabe por qué una lista de comprensión c() sería mucho más rápido que simplemente iterar sobre la lista a()

    – buscador_de_conocimiento

    7 de octubre de 2022 a las 0:28

Avatar de usuario de Ricky Robinson
robinson ricky

people = [
{'name': "Tom", 'age': 10},
{'name': "Mark", 'age': 5},
{'name': "Pam", 'age': 7}
]

def search(name):
    for p in people:
        if p['name'] == name:
            return p

search("Pam")

  • Agregué la función z() que implementa el siguiente como lo señaló Frédéric Hamidi arriba. Aquí están los resultados del perfil de Py.

    – león

    24 de marzo de 2019 a las 1:53

  • ¿Alguien sabe por qué una lista de comprensión c() sería mucho más rápido que simplemente iterar sobre la lista a()

    – buscador_de_conocimiento

    7 de octubre de 2022 a las 0:28

¿Alguna vez has probado el paquete pandas? Es perfecto para este tipo de tareas de búsqueda y también está optimizado.

import pandas as pd

listOfDicts = [
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]

# Create a data frame, keys are used as column headers.
# Dict items with the same key are entered into the same respective column.
df = pd.DataFrame(listOfDicts)

# The pandas dataframe allows you to pick out specific values like so:

df2 = df[ (df['name'] == 'Pam') & (df['age'] == 7) ]

# Alternate syntax, same thing

df2 = df[ (df.name == 'Pam') & (df.age == 7) ]

He agregado un poco de evaluación comparativa a continuación para ilustrar los tiempos de ejecución más rápidos de pandas en una escala mayor, es decir, más de 100k entradas:

setup_large="dicts = [];\
[dicts.extend(({ "name": "Tom", "age": 10 },{ "name": "Mark", "age": 5 },\
{ "name": "Pam", "age": 7 },{ "name": "Dick", "age": 12 })) for _ in range(25000)];\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(dicts);"

setup_small="dicts = [];\
dicts.extend(({ "name": "Tom", "age": 10 },{ "name": "Mark", "age": 5 },\
{ "name": "Pam", "age": 7 },{ "name": "Dick", "age": 12 }));\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(dicts);"

method1 = '[item for item in dicts if item["name"] == "Pam"]'
method2 = 'df[df["name"] == "Pam"]'

import timeit
t = timeit.Timer(method1, setup_small)
print('Small Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_small)
print('Small Method Pandas: ' + str(t.timeit(100)))

t = timeit.Timer(method1, setup_large)
print('Large Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_large)
print('Large Method Pandas: ' + str(t.timeit(100)))

#Small Method LC: 0.000191926956177
#Small Method Pandas: 0.044392824173
#Large Method LC: 1.98827004433
#Large Method Pandas: 0.324505090714

  • y method3 = “””df.query(“name == ‘Pam'”)”””, aunque es un poco más lento que el método 2 para conjuntos de datos pequeños (todavía 2 órdenes de magnitud más rápido que LC), es el doble de rápido en mi máquina para el conjunto de datos más grande

    – emmagras

    9 de febrero de 2022 a las 13:25

¿Ha sido útil esta solución?