Список поиска словарей Python

Предположим, у меня есть это:

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

и с помощью поиска "Pam" в качестве имени я хочу получить соответствующий словарь: {name: "Pam", age: 7}

Как этого добиться?

Ответ 1

Вы можете использовать выражение генератора:

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

Ответ 2

Это выглядит для меня наиболее питоническим образом:

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

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

результат (возвращается в виде списка в Python 2):

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

Примечание. В Python 3 возвращается объект фильтра. Таким образом, решение python3 будет:

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

Ответ 3

@Фредерик Хамиди ответ велик. В Python 3.x синтаксис для .next() слегка изменился. Таким образом, небольшая модификация:

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

Как упоминалось в комментариях @Matt, вы можете добавить значение по умолчанию как таковое:

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

Ответ 4

Вы можете использовать понимание :

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

Ответ 5

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

Ответ 6

Я протестировал различные методы, чтобы просмотреть список словарей и вернуть словари, где ключ x имеет определенное значение.

Результаты:

  • Speed: list comprehension> generator expression >> normal list iteration >>> filter.
  • Все масштабы линейны с количеством диктонов в списке (размер списка 10x → 10x раз).
  • Клавиши на словарь не влияют на скорость значительно для больших (тысяч) ключей. Посмотрите на этот график, который я вычислил: https://imgur.com/a/quQzv (имена методов см. Ниже).

Все тесты выполняются с помощью 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

Результаты:

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

Ответ 7

Чтобы добавить только маленький бит в @FrédéricHamidi.

Если вы не уверены, что ключ находится в списке dicts, что-то вроде этого поможет:

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

Ответ 8

Вы когда-нибудь пробовали пакет pandas? Он идеально подходит для такого рода задач поиска и оптимизирован.

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

Я добавил немного бенчмаркинга ниже, чтобы проиллюстрировать pandas 'более быстрое время работы в более крупном масштабе, то есть 100k + записи:

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

Ответ 9

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]

Это один из способов...

Ответ 10

Это общий способ поиска значения в списке словарей:

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

Ответ 11

Моя первая мысль заключалась в том, что вы можете подумать о создании словаря этих словарей... если, например, вы будете искать его более чем небольшое количество раз.

Однако это может быть преждевременная оптимизация. Что было бы не так:

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]

Ответ 12

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

Ответ 13

Просто используя понимание списка:

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

Образец кода:

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

Ответ 14

Вот сравнение, используя итерационный список throuhg, используя фильтр + лямбда или рефакторинг (при необходимости или действительный для вашего случая) ваш код для dict dict, а не список 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

И вывод следующий:

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

Вывод: Очевидно, что наличие словаря dicts является наиболее эффективным способом поиска в тех случаях, когда вы знаете, что вы будете искать только по id. интересно использовать фильтр - это самое медленное решение.

Ответ 15

Я нашел эту тему, когда искал ответ на тот же вопрос. Хотя я понимаю, что это поздний ответ, я думал, что внес свой вклад, если он будет полезен кому-либо еще:

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 __name__ == '__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

Ответ 16

Вы можете попробовать следующее:

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

Ответ 17

Я думаю, что вы можете использовать Панды, чтобы справиться с этим.

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

Это выводит:

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

Преимущества:

  • Pandas обеспечивает высокопроизводительную обработку данных, что означает, что если у вас большой набор данных, поиск не займет много времени.
  • Структура данных проста в использовании, и вы можете обрабатывать свои данные в виде таблицы для дальнейшего анализа.

Ответ 18

Вам нужно пройти все элементы списка. Нет ярлыка!

Если в другом месте вы не держите словарь имен, указывающих на элементы списка, но тогда вы должны позаботиться о последствиях появления элемента из вашего списка.