programing

Python에서 사전 목록 검색

powerit 2023. 4. 13. 21:16
반응형

Python에서 사전 목록 검색

지정:

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

색색으로 해야 하나요?name == "Pam"아래 해당 사전을 검색하려면 어떻게 해야 합니다.

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

제너레이터 식을 사용할 수 있습니다.

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

또한 항목 자체 대신 항목의 인덱스를 찾으려면 목록을 열거()할 수 있습니다.

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

내가 볼 때 이것은 가장 피조어적인 방법이다.

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

목록 이해를 사용할 수 있습니다.

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

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

결과:

  • 속도: 목록 이해 > 제너레이터 식 >> 일반 목록 반복 > > > 필터.
  • 모든 축척은 목록 내의 딕트 수에 따라 선형입니다(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
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")

판다 패키지를 사용해 본 적이 있나요?이러한 검색 작업에 적합하고 최적화되어 있습니다.

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

판다의 빠른 런타임을 보다 대규모로 설명하기 위해 아래에 벤치마킹을 조금 추가했습니다(예: 10만 개 이상의 엔트리).

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

@FrédéricHamidi에 아주 조금 더하기 위해서.

키가 딕트 목록에 있는지 확실하지 않은 경우 다음과 같은 방법을 사용하면 도움이 됩니다.

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

간단히 목록 이해 사용:

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

Python에서 필터와 다음 메서드를 사용하면 이를 달성할 수 있습니다.

filtermethod는 지정된 시퀀스를 필터링하고 반복기를 반환합니다. nextmethod는 반복자를 허용하고 목록의 다음 요소를 반환합니다.

그래서 당신은 그 요소를 찾을 수 있습니다.

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을 사용하다

목록 컴파션을 사용하는 간단한 방법 중 하나는 다음과 같습니다.l리스트입니다

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

그리고나서

[d['age'] for d in l if d['name']=='Tom']
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'}]

사전 목록에서 값을 검색하는 일반적인 방법은 다음과 같습니다.

def search_dictionaries(key, value, list_of_dictionaries):
    return [element for element in list_of_dictionaries if element[key] == value]
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'}
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]

이것도 한 가지 방법인데...

다음과 같이 시험해 보십시오.

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

수용된 답변을 쉽게 재사용할 수 있는 함수에 넣기

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

아니면 람다로도요

   get_item_lambda = lambda collection, key, target : next((item for item in collection if item[key] == target), None)

결과

    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}

키가 대상 사전에 없는 경우 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)

내 첫 번째 생각은 당신이 이 사전들의 사전을 만드는 것을 고려해 보는 것입니다. 예를 들어, 만약 당신이 사전을 몇 번 이상 검색한다면 말입니다.

그러나 이는 시기상조일 수 있습니다.문제점:

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]

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

  • 검색용으로 전달되는 키는 1개뿐이라고 가정하지만 복잡한 dict를 위해 더 많은 키를 갖는 것은 흥미로울 수 있습니다.
  • 검색을 위해 전달된 모든 키가 딕트에 존재한다고 가정하기 때문에 키 오류가 발생하지 않을 때 올바르게 처리되지 않습니다.

갱신된 제안:

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)

가장 비호조적인 것은 아닐지 몰라도, 적어도 조금 더 안전합니다.

사용방법:

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

요지.

다음은 반복적인 throug 목록을 사용한 비교입니다.필터+lambda를 사용하거나 코드를 리팩토링(필요하거나 유효한 경우)하여 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로만 검색할 수 있습니다.흥미롭게도 필터를 사용하는 것이 가장 느린 해결책이다.

나는 다음과 같은 받아쓰기를 만들 것이다.

names = ["Tom", "Mark", "Pam"]
ages = [10, 5, 7]
my_d = {}

for i, j in zip(names, ages):
    my_d[i] = {"name": i, "age": j}

또는 투고된 질문과 동일한 정보를 사용하여 다음을 수행합니다.

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

for d in info_list:
    my_d[d["name"]] = d

그럼 네가 할 수 있어my_d["Pam"]취득하다{"name": "Pam", "age": 7}

오리는 목록 이해나 필터보다 훨씬 더 빠를 것이다.오브젝트에 인덱스를 작성하기 때문에 모든 항목을 검색할 필요가 없습니다.

pip install ducks

from ducks import Dex

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

# Build the index
dex = Dex(dicts, {'name': str, 'age': int})

# Find matching objects
dex[{'name': 'Pam', 'age': 7}]

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

목록의 모든 요소를 검토해야 합니다.지름길은 없어!

목록 항목을 가리키는 이름의 사전을 다른 곳에 보관하고 있지만 목록에서 요소를 삭제했을 때의 결과를 처리해야 합니다.

나는 같은 질문에 대한 답을 찾다가 이 실마리를 찾았다.답변이 늦었다는 것을 알고 있지만, 다른 사람에게 도움이 될 수 있다면 기고해야겠다고 생각했습니다.

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

언급URL : https://stackoverflow.com/questions/8653516/search-a-list-of-dictionaries-in-python

반응형