Python词典搜索列表

Hel*_*nar 382 python search dictionary

假设我有这个:

[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]
Run Code Online (Sandbox Code Playgroud)

并通过搜索"Pam"作为名称,我想检索相关字典:{name:"Pam",年龄:7}

怎么做到这一点?

Fré*_*idi 425

您可以使用生成器表达式:

>>> 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'}
Run Code Online (Sandbox Code Playgroud)

  • 只是为了节省其他人一点时间,如果你需要一个默认值,事件"Pam"只是不在列表中:next((项目中的项目如果项目["name"] =="Pam") , 没有) (213认同)
  • 如果字典中不存在键,这将引发stopiteration错误 (7认同)
  • @Siemkowski:然后添加`enumerate()` 以生成运行索引:`next(i for i, item in enumerate(dicts) if item["name"] == "Pam")`。 (3认同)
  • `[item for item in dicts if item["name"] == "Pam"][0]` 怎么样? (2认同)
  • @Moberg,它仍然是一个列表理解,因此无论匹配项的位置如何,它都将迭代整个输入序列. (2认同)
  • 假设我想获取匹配字典的索引,是否有比`dicts.index(next(item for item in dicts if item["name"] == "Pam"))`更简洁的解决方案? (2认同)

小智 187

这对我来说是最pythonic的方式:

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

filter(lambda person: person['name'] == 'Pam', people)
Run Code Online (Sandbox Code Playgroud)

结果(在Python 2中作为列表返回):

[{'age': 7, 'name': 'Pam'}]
Run Code Online (Sandbox Code Playgroud)

注意:在Python 3中,返回一个过滤器对象.所以python3解决方案将是:

list(filter(lambda person: person['name'] == 'Pam', people))
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是,这个答案会返回一个列表,其中包含人们对"Pam"的所有匹配,或者我们可以通过将比较运算符更改为!=来获得所有不是"Pam"的人的列表.+1 (13认同)
  • 获取第一个匹配项:`next(filter(lambda x: x['name'] == 'Pam', dicts))` (5认同)
  • 另外值得一提的是结果是一个过滤器对象,而不是一个列表 - 如果你想使用像`len()`这样的东西,你需要先对结果调用`list()`.或者:/sf/ask/1342753191/ (2认同)

Mik*_*e N 55

@FrédéricHamidi的答案很棒.在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'}
Run Code Online (Sandbox Code Playgroud)

正如@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
>>>
Run Code Online (Sandbox Code Playgroud)

  • 这是 Python 3.x 的最佳答案。如果您需要字典中的特定元素,例如年龄,您可以编写: next((item.get('age') for item in dicts if item["name"] == "Pam"), False) (2认同)

小智 41

您可以使用列表理解:

def search(name, people):
    return [element for element in people if element['name'] == name]
Run Code Online (Sandbox Code Playgroud)

  • 这很好,因为如果有多个匹配则返回所有匹配.不完全是问题所要求的,但这正是我所需要的!谢谢! (4认同)
  • 另请注意,这会返回一个列表! (2认同)

sat*_*oru 29

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")
Run Code Online (Sandbox Code Playgroud)

  • 只是为了使这个非常有用的例程更通用:`def搜索(列表,键,值):对于列表中的项:if item [key] == value:return item` (5认同)

use*_*036 21

我测试了各种方法来浏览字典列表并返回键x具有特定值的字典.

结果:

  • 速度:列表理解>生成器表达>>普通列表迭代>>>过滤.
  • 所有比例与列表中的dicts数量成线性关系(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
Run Code Online (Sandbox Code Playgroud)

结果:

1.7303 # normal list iteration 
1.3849 # generator expression 
1.3158 # list comprehension 
7.7848 # filter
Run Code Online (Sandbox Code Playgroud)


小智 11

def dsearch(lod, **kw):
    return filter(lambda i: all((i[k] == v for (k, v) in kw.items())), lod)

lod=[{'a':33, 'b':'test2', 'c':'a.ing333'},
     {'a':22, 'b':'ihaha', 'c':'fbgval'},
     {'a':33, 'b':'TEst1', 'c':'s.ing123'},
     {'a':22, 'b':'ihaha', 'c':'dfdvbfjkv'}]



list(dsearch(lod, a=22))

[{'a': 22, 'b': 'ihaha', 'c': 'fbgval'},
 {'a': 22, 'b': 'ihaha', 'c': 'dfdvbfjkv'}]



list(dsearch(lod, a=22, b='ihaha'))

[{'a': 22, 'b': 'ihaha', 'c': 'fbgval'},
 {'a': 22, 'b': 'ihaha', 'c': 'dfdvbfjkv'}]


list(dsearch(lod, a=22, c='fbgval'))

[{'a': 22, 'b': 'ihaha', 'c': 'fbgval'}]
Run Code Online (Sandbox Code Playgroud)


Dra*_*rch 10

添加一点点@FrédéricHamidi.

如果您不确定某个键位于dicts列表中,这样的内容会有所帮助:

next((item for item in dicts if item.get("name") and item["name"] == "Pam"), None)
Run Code Online (Sandbox Code Playgroud)

  • 或者简单地 `item.get("name") == "Pam"` (3认同)

abb*_*obh 9

你有没有试过熊猫包?它非常适合这种搜索任务并进行优化.

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) ]
Run Code Online (Sandbox Code Playgroud)

我在下面添加了一些基准测试来说明大熊猫在更大规模上的运行时间更快,即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
Run Code Online (Sandbox Code Playgroud)


rob*_*ing 7

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'}
Run Code Online (Sandbox Code Playgroud)


Man*_*r S 7

您可以通过在 Python 中使用 filter 和 next 方法来实现这一点。

filter方法过滤给定的序列并返回一个迭代器。 next方法接受一个迭代器并返回列表中的下一个元素。

所以你可以通过以下方式找到元素,

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

next(filter(lambda obj: obj.get('name') == 'Pam', my_dict), None)
Run Code Online (Sandbox Code Playgroud)

输出是,

{'name': 'Pam', 'age': 7}
Run Code Online (Sandbox Code Playgroud)

注意:None如果没有找到我们正在搜索的名称,上面的代码将返回incase。


cvg*_*cvg 7

使用列表推导式的一种简单方法是,如果 l是列表

l = [
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]
Run Code Online (Sandbox Code Playgroud)

然后

[d['age'] for d in l if d['name']=='Tom']
Run Code Online (Sandbox Code Playgroud)


Fed*_*Baù 7

将接受的答案放入函数中以便于重复使用

def get_item(collection, key, target):
    return next((item for item in collection if item[key] == target), None)
Run Code Online (Sandbox Code Playgroud)

或者也可以作为 lambda

   get_item_lambda = lambda collection, key, target : next((item for item in collection if item[key] == target), None)
Run Code Online (Sandbox Code Playgroud)

结果

    key = "name"
    target = "Pam"
    print(get_item(target_list, key, target))
    print(get_item_lambda(target_list, key, target))

    #{'name': 'Pam', 'age': 7}
    #{'name': 'Pam', 'age': 7}
Run Code Online (Sandbox Code Playgroud)

如果键可能不在目标字典中,请使用 dict.get 并避免KeyError

def get_item(collection, key, target):
    return next((item for item in collection if item.get(key, None) == target), None)

get_item_lambda = lambda collection, key, target : next((item for item in collection if item.get(key, None) == target), None)
Run Code Online (Sandbox Code Playgroud)


Nic*_*son 6

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]
Run Code Online (Sandbox Code Playgroud)

这是一种方式......


ipe*_*sus 6

这是在字典列表中搜索值的一般方法:

def search_dictionaries(key, value, list_of_dictionaries):
    return [element for element in list_of_dictionaries if element[key] == value]
Run Code Online (Sandbox Code Playgroud)


Sid*_*thy 6

你可以试试这个:

''' 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'} 
Run Code Online (Sandbox Code Playgroud)


Jim*_*nis 5

我的第一个想法是,您可能需要考虑创建这些字典的字典......例如,如果您要搜索它的次数不少于一次。

然而,这可能是一个不成熟的优化。会出现什么问题:

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]
Run Code Online (Sandbox Code Playgroud)


Teo*_*tic 5

只需使用列表推导:

[i for i in dct if i['name'] == 'Pam'][0]
Run Code Online (Sandbox Code Playgroud)

样例代码:

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'}
Run Code Online (Sandbox Code Playgroud)


one*_*sec 5

这里提出的大多数(如果不是全部)实现都有两个缺陷:

  • 他们假设只传递一个键来进行搜索,而对于复杂的字典来说,拥有更多的键可能会很有趣
  • 他们假设用于搜索的所有键都存在于字典中,因此当不存在时,他们无法正确处理发生的 KeyError 。

更新的提议:

def find_first_in_list(objects, **kwargs):
    return next((obj for obj in objects if
                 len(set(obj.keys()).intersection(kwargs.keys())) > 0 and
                 all([obj[k] == v for k, v in kwargs.items() if k in obj.keys()])),
                None)
Run Code Online (Sandbox Code Playgroud)

也许不是最Pythonic,但至少更安全一点。

用法:

>>> obj1 = find_first_in_list(list_of_dict, name='Pam', age=7)
>>> obj2 = find_first_in_list(list_of_dict, name='Pam', age=27)
>>> obj3 = find_first_in_list(list_of_dict, name='Pam', address='nowhere')
>>> 
>>> print(obj1, obj2, obj3)
{"name": "Pam", "age": 7}, None, {"name": "Pam", "age": 7}
Run Code Online (Sandbox Code Playgroud)

要旨。