사전 검색의 파이썬 목록


449

내가 이것을 가지고 있다고 가정 :

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

"Pam"을 이름으로 검색하여 관련 사전을 검색하려고합니다. {name: "Pam", age: 7}

이것을 달성하는 방법?

답변:


514

생성기 표현식을 사용할 수 있습니다 .

>>> 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가 사용자가 제안한 내용을 수행 하고 약간 다른 API를 사용하여 기본값을 제공 할 수 있습니다 .

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

그리고 항목 자체가 아닌 항목의 색인을 찾으 려면 목록을 enumerate () 할 수 있습니다 .

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

230
"Pam"이벤트에 기본값이 필요한 경우 목록에없는 다른 사용자를 조금만 저장하십시오. next ((item [ "name"] == "Pam"인 경우 dicts의 항목 항목) , 없음)
Matt

1
무엇에 대해 [item for item in dicts if item["name"] == "Pam"][0]?
Moberg

3
@Moberg는 여전히 목록 이해이므로 일치하는 항목의 위치에 관계없이 전체 입력 시퀀스를 반복합니다.
Frédéric Hamidi

7
사전에 키가 없으면 중지 오류가 발생합니다
Kishan

3
@Siemkowski : 그런 다음 추가 enumerate()하여 실행중인 색인을 생성하십시오 next(i for i, item in enumerate(dicts) if item["name"] == "Pam").
Martijn Pieters

218

이것은 나에게 가장 파이썬적인 방법으로 보입니다.

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

14
이 답변은 사람들의 'Pam'에 대한 모든 일치하는 목록을 반환한다는 점에 주목할 필요가 있습니다. 대신 비교 연산자를! =로 변경하여 'Pam'이 아닌 모든 사람들의 목록을 얻을 수도 있습니다. +1
Onema

2
또한 결과가 목록이 아닌 필터 객체라는 점을 언급 할 가치가 있습니다.과 같은 것을 사용하려면 먼저 결과 len()를 호출해야합니다 list(). 또는 : stackoverflow.com/questions/19182188/…
wasabigeek

@wasabigeek 이것은 내 파이썬 2.7의 말입니다 : people = [{ 'name': "Tom", 'age': 10}, { 'name': "Mark", 'age': 5}, { 'name': "팸"연령 '7}] = R 필터 (람다 사람 : 사람 ['이름 '] =='파멜라 '명) 유형 (R)에서 그래서 rA는list
PaoloC

1
목록 함축보다 파이썬 맵 / 필터 / 감소보다 간주됩니다 : stackoverflow.com/questions/5426754/google-python-style-guide
JRC

2
첫 경기를 가져 오기 :next(filter(lambda x: x['name'] == 'Pam', dicts))
xgMz

60

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

1
이것이 Python 3.x에 대한 최상의 답변입니다. 당신은 dicts에서 특정 요소가 필요한 경우, 나이처럼, 당신은 쓸 수 있습니다 : 다음 ((item.get (dicts의 항목에 대해 '나이')의 경우 항목 [ "이름"] == "팸"), 거짓)
cwhisperer

47

당신은 목록 이해를 사용할 수 있습니다 :

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

4
둘 이상의 일치하는 경우 모든 일치 항목을 반환하기 때문에 좋습니다. 질문이 정확히 요구 한 것이 아니라 내가 필요한 것입니다! 감사!
user3303554

또한 이것은 목록을 반환합니다!
Abbas

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

주어진 이름으로 목록의 첫 번째 사전을 반환합니다.
Ricky Robinson

5
이 유용한 루틴을 좀 더 일반적으로 만들기 위해 :def search(list, key, value): for item in list: if item[key] == value: return item
Jack James

30

사전 목록을 살펴보고 키 x에 특정 값이있는 사전을 반환하기 위해 다양한 방법을 테스트했습니다.

결과 :

  • 속도 : 목록 이해> 생성기 표현 >> 일반 목록 반복 >>> 필터.
  • 모든 척도는 목록의 dict 수에 따라 선형입니다 (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

Frédéric Hamidi가 지적한대로 다음을 구현하는 z () 함수를 추가했습니다. Py 프로필의 결과는 다음과 같습니다.
leon

10

@ FrédéricHamidi에 약간만 추가하십시오.

dicts 목록에 키가 확실하지 않은 경우 다음과 같이 도움이 될 것입니다.

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

또는 간단히item.get("name") == "Pam"
Andreas Haferburg

10

팬더 패키지를 사용해 본 적이 있습니까? 이러한 종류의 검색 작업에 적합하며 최적화되었습니다.

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

나는 팬더의 더 빠른 런타임을 더 큰 규모, 즉 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

7

사전 목록에서 값을 검색하는 일반적인 방법입니다.

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

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]

이것은 한 가지 방법입니다 ...


1
"name"키가없는 "names"의 모든 항목을 정상적으로 처리하려면 [d.get ( 'name', '') == 'Pam'] 인 경우 x에서 d를 제안 할 수 있습니다.
Jim Dennis

6

간단히 목록 이해를 사용하십시오.

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

5

파이썬에서 필터와 다음 메소드를 사용하여이를 달성 할 수 있습니다.

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)

출력은

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

참고 : 위의 코드 None는 검색하는 이름을 찾지 못하면 incase 를 반환 합니다.


이것은 목록 이해력보다 훨씬 느립니다.
AnupamChugh

4

내 첫 번째 생각은 당신이 이러한 사전의 사전을 만드는 것을 고려하고 싶을 것입니다.

그러나 그것은 조기 최적화 일 수 있습니다. 무엇이 문제일까요 :

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]

실제로 name = None 항목이 포함 된 사전을 가질 수 있습니다. 그러나 그것은 실제로이 목록 이해와 함께 작동하지 않으며 아마도 데이터 저장소에서 허용하지 않는 것이 좋습니다.
Jim Dennis

1
디버그 모드가 꺼져 있으면 어설 션을 건너 뛸 수 있습니다.
bluppfisk 2016 년

4
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

목록 이해를 사용하는 간단한 방법 중 하나 l는 목록입니다.

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

그때

[d['age'] for d in l if d['name']=='Tom']

2

당신은 이것을 시도 할 수 있습니다 :

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

1

다음은 반복 룰 목록을 사용하고 필터 + 람다 또는 리팩토링 (필요하거나 경우에 유효한 경우)을 사용하여 코드를 dict 목록이 아닌 dict의 dict로 비교하는 것입니다.

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

결론 : 딕셔너리 사전을 갖는 것이 ID로만 검색한다고 말하는 경우에 가장 효율적으로 검색 할 수있는 방법입니다. 흥미롭게도 필터를 사용하는 것이 가장 느린 솔루션입니다.


1

여기에 제안 된 대부분의 (모두는 아니지만) 구현에는 두 가지 결함이 있습니다.

  • 그들은 검색을 위해 하나의 키만 전달한다고 가정하지만 복잡한 dict에는 더 많은 키를 갖는 것이 흥미로울 수 있습니다
  • 그들은 검색을 위해 전달 된 모든 키가 dicts에 있다고 가정하므로 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)

아마도 가장 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}

요점 .


0

목록의 모든 요소를 ​​거쳐야합니다. 바로 가기가 없습니다!

다른 곳을 제외하고는 목록의 항목을 가리키는 이름 사전을 유지하지만 목록에서 요소를 팝업하는 결과를 처리해야합니다.


정렬되지 않은 목록과 누락 된 키의 경우이 문장은 정확하지만 일반적으로 그렇지 않습니다. 목록이 정렬 된 것으로 알려진 경우 모든 요소를 ​​반복 할 필요가 없습니다. 또한 단일 레코드에 도달하고 키가 고유하거나 하나의 요소 만 필요한 경우 단일 항목이 리턴되면 반복이 중지 될 수 있습니다.
user25064

@ user334856의 답변보기
Melih Yıldız

@ MelihYıldız '어쩌면 나는 내 진술에 명확하지 않았다. 답변에 목록 이해 user334856을 사용하면 stackoverflow.com/a/8653572/512225 가 전체 목록을 통과합니다. 이것은 내 진술을 확인합니다. 당신이 말하는 대답은 내가 쓴 것을 말하는 또 다른 방법입니다.
jimifiki

0

동일한 질문에 대한 답변을 검색 할 때이 스레드를 찾았습니다. 답변이 늦다는 것을 알고 있지만 다른 사람에게 도움이 될 경우에 도움이 될 것이라고 생각했습니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.