web-dev-qa-db-fra.com

Recherche dans la liste des dictionnaires en python

Supposons que j'ai ceci:

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

et en recherchant "Pam" comme nom, je souhaite récupérer le dictionnaire correspondant: {name: "Pam", age: 7}

Comment y parvenir?

294
Hellnar

Vous pouvez utiliser une expression generator :

>>> 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'}
342
Frédéric Hamidi

Cela me semble le plus pythonique:

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

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

résultat (renvoyé sous forme de liste dans Python 2):

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

Remarque: En Python 3, un objet filtre est renvoyé. La solution python3 serait donc:

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

@ La réponse de Frédéric Hamidi est excellente. Dans Python 3.x, la syntaxe de .next() a légèrement changé. Donc une légère modification:

>>> 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'}

Comme mentionné dans les commentaires de @Matt, vous pouvez ajouter une valeur par défaut en tant que telle:

>>> 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
>>>
47
Mike N

Vous pouvez utiliser une compréhension list :

def search(name, people):
    return [element for element in people if element['name'] == name]
33
user334856
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")
21
satoru

J'ai testé diverses méthodes pour parcourir une liste de dictionnaires et renvoyer les dictionnaires dans lesquels la clé x a une certaine valeur. 

Résultats: 

  • Vitesse: compréhension de la liste> expression du générateur >> itération de la liste normale >>> filtre. 
  • Toutes les échelles linéaires avec le nombre de dict de la liste (taille de la liste 10x -> temps 10x). 
  • Les clés par dictionnaire n'affectent pas la vitesse de manière significative pour des quantités importantes (des milliers) de clés. Veuillez voir ce graphique que j'ai calculé: https://imgur.com/a/quQzv (noms de méthodes voir ci-dessous). 

Tous les tests effectués avec Python 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

Résultats:

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

Pour ajouter juste un petit peu à @ FrédéricHamidi.

Si vous n’êtes pas sûr qu’une clé figure dans la liste des repères, voici ce qui pourrait vous aider:

next((item for item in dicts if item.get("name") and item["name"] == "Pam"), None)
9
Drazen Urch

Avez-vous déjà essayé le paquet de pandas? C'est parfait pour ce genre de tâche de recherche et optimisé aussi.

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) ]

J'ai ajouté un peu d'analyse comparative ci-dessous pour illustrer les exécutions plus rapides des pandas à plus grande échelle, c'est-à-dire plus de 100 000 entrées:

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
7
abby sobh
names = [{'name':'Tom', 'age': 10}, {'name': 'Mark', 'age': 5}, {'name': 'Pam', 'age': 7}]
resultlist = [d    for d in names     if d.get('name', '') == 'Pam']
first_result = resultlist[0]

C'est une façon ...

6
Niclas Nilsson

C'est une manière générale de rechercher une valeur dans une liste de dictionnaires:

def search_dictionaries(key, value, list_of_dictionaries):
    return [element for element in list_of_dictionaries if element[key] == value]
6
ipegasus

Ma première pensée serait que vous souhaitiez peut-être créer un dictionnaire de ces dictionnaires ... si, par exemple, vous alliez le chercher plus souvent qu'un petit nombre de fois.

Cependant, cela pourrait être une optimisation prématurée. Quel serait le problème avec:

def get_records(key, store=dict()):
    '''Return a list of all records containing name==key from our store
    '''
    assert key is not None
    return [d for d in store if d['name']==key]
4
Jim Dennis
dicts=[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]

from collections import defaultdict
dicts_by_name=defaultdict(list)
for d in dicts:
    dicts_by_name[d['name']]=d

print dicts_by_name['Tom']

#output
#>>>
#{'age': 10, 'name': 'Tom'}
3
robert king

Simplement en utilisant la compréhension de liste:

[i for i in dct if i['name'] == 'Pam'][0]

Exemple de code:

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

print([i for i in dct if i['name'] == 'Pam'][0])

> {'age': 7, 'name': 'Pam'}
1
Teoretic

Vous devez parcourir tous les éléments de la liste. Il n'y a pas de raccourci!

Sauf si, ailleurs, vous gardez un dictionnaire des noms pointant vers les éléments de la liste, vous devez alors vous occuper des conséquences de la suppression d'un élément de votre liste.

0
jimifiki

Vous pouvez essayer ceci:

''' lst: list of dictionaries '''
lst = [{"name": "Tom", "age": 10}, {"name": "Mark", "age": 5}, {"name": "Pam", "age": 7}]

search = raw_input("What name: ") #Input name that needs to be searched (say 'Pam')

print [ lst[i] for i in range(len(lst)) if(lst[i]["name"]==search) ][0] #Output
>>> {'age': 7, 'name': 'Pam'} 
0
Siddharth Satpathy

J'ai trouvé ce fil lorsque je cherchais une réponse à la même question Bien que je me rende compte que c’est une réponse tardive, j’ai pensé que j’y contribuerais au cas où cela serait utile à quiconque:

def find_dict_in_list(dicts, default=None, **kwargs):
    """Find first matching :obj:`dict` in :obj:`list`.

    :param list dicts: List of dictionaries.
    :param dict default: Optional. Default dictionary to return.
        Defaults to `None`.
    :param **kwargs: `key=value` pairs to match in :obj:`dict`.

    :returns: First matching :obj:`dict` from `dicts`.
    :rtype: dict

    """

    rval = default
    for d in dicts:
        is_found = False

        # Search for keys in dict.
        for k, v in kwargs.items():
            if d.get(k, None) == v:
                is_found = True

            else:
                is_found = False
                break

        if is_found:
            rval = d
            break

    return rval


if __== '__main__':
    # Tests
    dicts = []
    keys = 'spam eggs shrubbery knight'.split()

    start = 0
    for _ in range(4):
        dct = {k: v for k, v in Zip(keys, range(start, start+4))}
        dicts.append(dct)
        start += 4

    # Find each dict based on 'spam' key only.  
    for x in range(len(dicts)):
        spam = x*4
        assert find_dict_in_list(dicts, spam=spam) == dicts[x]

    # Find each dict based on 'spam' and 'shrubbery' keys.
    for x in range(len(dicts)):
        spam = x*4
        assert find_dict_in_list(dicts, spam=spam, shrubbery=spam+2) == dicts[x]

    # Search for one correct key, one incorrect key:
    for x in range(len(dicts)):
        spam = x*4
        assert find_dict_in_list(dicts, spam=spam, shrubbery=spam+1) is None

    # Search for non-existent dict.
    for x in range(len(dicts)):
        spam = x+100
        assert find_dict_in_list(dicts, spam=spam) is None
0
Doug R.

Je pense que vous pouvez utiliser des pandas pour régler ce problème.

import pandas as pd

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

person_df = pd.DataFrame(person_list)
person_df[person_df["name"] == "Pam"].to_dict('records')

Il produit:

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

Les avantages sont:

  • Les pandas fournissent un traitement de données haute performance, ce qui signifie que si vous avez un grand ensemble de données, la recherche ne prendra pas beaucoup de temps.
  • La structure de données est facile à utiliser et vous pouvez traiter vos données comme un tableau pour une analyse plus approfondie.
0
lazy_frog

Voici une comparaison utilisant la liste itérative, en utilisant le filtre + lambda ou le refactoring (si nécessaire ou valable pour votre cas) votre code pour dicter des dict plutôt que de la liste de dicts

import time

# Build list of dicts
list_of_dicts = list()
for i in range(100000):
    list_of_dicts.append({'id': i, 'name': 'Tom'})

# Build dict of dicts
dict_of_dicts = dict()
for i in range(100000):
    dict_of_dicts[i] = {'name': 'Tom'}


# Find the one with ID of 99

# 1. iterate through the list
lod_ts = time.time()
for elem in list_of_dicts:
    if elem['id'] == 99999:
        break
lod_tf = time.time()
lod_td = lod_tf - lod_ts

# 2. Use filter
f_ts = time.time()
x = filter(lambda k: k['id'] == 99999, list_of_dicts)
f_tf = time.time()
f_td = f_tf- f_ts

# 3. find it in dict of dicts
dod_ts = time.time()
x = dict_of_dicts[99999]
dod_tf = time.time()
dod_td = dod_tf - dod_ts


print 'List of Dictionries took: %s' % lod_td
print 'Using filter took: %s' % f_td
print 'Dict of Dicts took: %s' % dod_td

Et la sortie est la suivante:

List of Dictionries took: 0.0099310874939
Using filter took: 0.0121960639954
Dict of Dicts took: 4.05311584473e-06

Conclusion: Avoir un dictionnaire de dict est clairement le moyen le plus efficace de pouvoir effectuer une recherche dans les cas où vous savez que vous effectuerez une recherche par identifiant uniquement. Solution.

0
Kőhalmy Zoltán