Поиск в списке словарей в Python
Описание проблемы:
У меня есть массив словарей, который выглядит следующим образом:
[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]
Мне нужно выполнить поиск по имени, чтобы найти словарь, где name
равен "Pam". Как мне это сделать и получить соответствующий словарь:
{"name": "Pam", "age": 7}
Я работаю с Python и не могу понять, как правильно осуществить этот поиск. Можете ли вы помочь с примером кода?
5 ответ(ов)
Это, на мой взгляд, самый питоничный способ:
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 возвращается объект фильтра. Таким образом, решение для Python 3 будет выглядеть так:
list(filter(lambda person: person['name'] == 'Pam', people))
Ответ @Frédéric Hamidi отличный. В 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
>>>
Я протестировал различные методы обхода списка словарей и возврата тех словарей, где ключ x имеет определённое значение.
Результаты:
- Скорость: списковое включение > генераторное выражение >> обычная итерация по списку >>> фильтр.
- Все методы масштабируются линейно с количеством словарей в списке (увеличение размера списка в 10 раз → увеличение времени выполнения в 10 раз).
- Количество ключей в словаре не оказывает значительного влияния на скорость при большом количестве ключей (тысячи). Посмотрите на этот график, который я составил: ссылка на график (названия методов смотрите ниже).
Все тесты проводились на Python 3.6.4, W7x64.
from random import randint
from timeit import timeit
list_dicts = []
for _ in range(1000): # количество словарей в списке
dict_tmp = {}
for i in range(10): # количество ключей для каждого словаря
dict_tmp[f"key{i}"] = randint(0,50)
list_dicts.append( dict_tmp )
def a():
# обычная итерация по всем элементам
for dict_ in list_dicts:
if dict_["key3"] == 20:
pass
def b():
# использование 'генератора'
for dict_ in (x for x in list_dicts if x["key3"] == 20):
pass
def c():
# использование 'списка'
for dict_ in [x for x in list_dicts if x["key3"] == 20]:
pass
def d():
# использование 'фильтра'
for dict_ in filter(lambda x: x['key3'] == 20, list_dicts):
pass
Результаты:
1.7303 # обычная итерация по списку
1.3849 # генераторное выражение
1.3158 # списковое включение
7.7848 # фильтр
Ваш код делает именно то, что вы ожидаете, т.е. ищет человека по имени в списке people
. Функция search
проходит по каждому элементу списка и сравнивает значение ключа 'name'
с переданным аргументом name
. Если находит совпадение, то возвращает соответствующий словарь.
Вот ваш код на русском:
people = [
{'name': "Том", 'age': 10},
{'name': "Марк", 'age': 5},
{'name': "Пэм", 'age': 7}
]
def search(name):
for p in people:
if p['name'] == name:
return p
search("Пэм")
Функция search("Пэм")
вернет словарь {'name': "Пэм", 'age': 7}
, так как именно такой элемент присутствует в списке people
. Если вы хотите учесть возможные ситуации, когда имя не найдено, вы можете доработать функцию, добавив обработку этого случая, например:
def search(name):
for p in people:
if p['name'] == name:
return p
return None # Вернуть None, если имя не найдено
Таким образом, если вызвать search("Джон")
, вы получите None
, что указывает на отсутствие результата.
Да, я пробовал использовать пакет pandas, и он действительно отлично подходит для таких задач поиска, а также оптимизирован по производительности.
Вот пример использования pandas для работы с данными в виде словарей:
import pandas as pd
listOfDicts = [
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]
# Создаем DataFrame, ключи используются в качестве заголовков столбцов.
# Элементы словаря с одинаковым ключом попадают в один и тот же столбец.
df = pd.DataFrame(listOfDicts)
# DataFrame pandas позволяет извлекать конкретные значения вот так:
df2 = df[(df['name'] == 'Pam') & (df['age'] == 7)]
# Альтернативный синтаксис, тот же результат
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('Маленький метод LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_small)
print('Маленький метод Pandas: ' + str(t.timeit(100)))
t = timeit.Timer(method1, setup_large)
print('Большой метод LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_large)
print('Большой метод Pandas: ' + str(t.timeit(100)))
# Маленький метод LC: 0.000191926956177
# Маленький метод Pandas: 0.044392824173
# Большой метод LC: 1.98827004433
# Большой метод Pandas: 0.324505090714
Как видно из результатов, pandas демонстрирует явное преимущество по времени выполнения при работе с большими наборами данных.
Создание словаря с помощью генератора словарей
Преобразование списка словарей в DataFrame pandas
Следует ли использовать 'has_key()' или 'in' для проверки наличия ключа в словарях Python?
Как преобразовать вложенный словарь Python в объект?
Ошибка: "'dict' объект не имеет метода 'iteritems'"