dict의 값을 일치시켜 목록에서 dict의 색인을 찾으십시오.


131

dicts 목록이 있습니다.

list = [{'id':'1234','name':'Jason'},
        {'id':'2345','name':'Tom'},
        {'id':'3456','name':'Art'}]

name = 'Tom'과 일치하여 인덱스 위치 [0], [1] 또는 [2]를 어떻게 효율적으로 찾을 수 있습니까?

이것이 1 차원 목록이라면 list.index () 할 수는 있지만 목록 내 dicts 값을 검색하여 진행하는 방법을 모르겠습니다.


6
"list"는 목록 생성자이므로 목록의 다른 이름을 선택하는 것이 좋습니다 (예를 들어도). 그리고 요소가 없으면 응답은 무엇입니까? 예외를 제기? 반환 없음?
tokland

7
이것을 많이 필요로한다면보다 적절한 데이터 구조 (아마도 { 'Jason': {'id': '1234'}, 'Tom': {'id': '1245'}, ...}?)를 사용하십시오

3
@delnan 재난의 요리법이기 때문에! 있다면,이어야합니다 {'1234': {'name': 'Jason'}, ...}. 이것이 유스 케이스에 도움이되지는 않습니다.
OJFord

답변:


145
tom_index = next((index for (index, d) in enumerate(lst) if d["name"] == "Tom"), None)
# 1

이름에서 반복적으로 가져와야하는 경우 사전을 사용하여 이름별로 색인을 작성해야합니다. 이렇게 하면 작업이 O (1) 시간이됩니다. 아이디어:

def build_dict(seq, key):
    return dict((d[key], dict(d, index=index)) for (index, d) in enumerate(seq))

info_by_name = build_dict(lst, key="name")
tom_info = info_by_name.get("Tom")
# {'index': 1, 'id': '2345', 'name': 'Tom'}

2
IMHO 이것은 읽을 수 없거나 Pythonic은 @Emile의 답변입니다. 의도는 실제로 생성기를 생성하는 것이 아니기 때문에 (그리고 next()이것을 사용 하는 것이 이상하게 보입니다), 목표는 색인을 얻는 것입니다. 또한 이것은 StopIteration을 발생시키는 반면 Python lst.index()메소드는 ValueError를 발생시킵니다.
Ben Hoyt

@ benhoyt : StopIteration 예외도 좋아하지 않지만 next ()의 기본값을 변경할 수는 있지만 발생하는 예외는 수정되었습니다. 파이썬은 다소 주관적이므로 논쟁하지 않을 것입니다. 아마 for-loop가 더 파이썬 적입니다. 반면에 일부 사람들은 first ()에 대해 next ()의 별칭을 지정합니다. first (index for (index, d) in ...).
tokland

first()더 잘 들립니다. 항상 StopIteration을 시도 / 제외하고 ValueError를 발생시켜 호출자가 일관성을 유지할 수 있습니다. 또는 next()의 기본값을 -1로 설정하십시오.
Ben Hoyt

1
@ gdw2 : SyntaxError: Generator expression must be parenthesized if not sole argument그렇게 할 때 얻습니다 .
avoliva

2
@avoliva 다음과 같이 괄호를 추가합니다next((index for (index, d) in enumerate(lst) if d["name"] == "Tom"), None)
HussienK

45

읽을 수있는 간단한 버전은

def find(lst, key, value):
    for i, dic in enumerate(lst):
        if dic[key] == value:
            return i
    return -1

8
이것은 가장 읽기 쉽고 파이썬적인 것 같습니다. 그것은 또한 str.find()좋은 행동을 모방합니다 . 원하는 경우 -1을 반환 index()하는 ValueError대신 호출하여 a 를 올릴 수도 있습니다 .
Ben Hoyt

6
합의-일치하는 항목이 없을 때 -1을 반환하면 항상 목록에서 마지막 dict를 얻을 수 있습니다. None을 반환하고 호출 코드에서 일치하는 것이 있는지 확인하는 것이 좋습니다.
shacker

9

목록의 모든 항목을 확인해야하므로 효율적이지 않습니다 (O (n)). 효율성을 원한다면 dict of dicts를 사용할 수 있습니다 . 질문에, 그것을 찾을 수있는 한 가지 방법이 있습니다 (그러나이 데이터 구조를 고수하고 싶다면 실제로 Brent Newey가 의견에 쓴 것처럼 생성기사용하는 것이 더 효율적입니다 ; tokland의 답변 참조).

>>> L = [{'id':'1234','name':'Jason'},
...         {'id':'2345','name':'Tom'},
...         {'id':'3456','name':'Art'}]
>>> [i for i,_ in enumerate(L) if _['name'] == 'Tom'][0]
1

1
발전기를 사용하여 원하는 효율성을 얻을 수 있습니다. tokland의 답변을 참조하십시오.
Brent Newey

2
@Brent Newey : 생성기는 사실을 바꾸지 않습니다. 전체 목록을 가로 질러 검색해야한다고 주장하는대로 O (n)을 검색 할 수 있습니다 ... 목록의 길이에 따라 생성기 사용과 사용의 차이 dict를 사용하는 것과리스트를 사용하는 것의 차이는 그렇지 않을 수도 있지만 for 루프 또는 무시할 수있는 것
Dirk

@ 브렌트 : 당신이 맞지만, 검색 된 항목이 목록의 끝에 있으면 사전에서 O (1) 조회를 이길 수 있습니까?
aeter

1
@Dirk 일치하는 것이 발견되면 생성기의 next () 호출이 중지되므로 전체 목록을 순회 할 필요가 없습니다.
Brent Newey

@aeter 당신은 공정한 지적을합니다. 나는 경기가 발견되면 멈출 수 있다고 언급했다.
Brent Newey

2

다음은 사전의 색인 위치가있는 경우이를 찾는 함수입니다.

dicts = [{'id':'1234','name':'Jason'},
         {'id':'2345','name':'Tom'},
         {'id':'3456','name':'Art'}]

def find_index(dicts, key, value):
    class Null: pass
    for i, d in enumerate(dicts):
        if d.get(key, Null) == value:
            return i
    else:
        raise ValueError('no dict with the key and value combination found')

print find_index(dicts, 'name', 'Tom')
# 1
find_index(dicts, 'name', 'Ensnare')
# ValueError: no dict with the key and value combination found

2

필터 / 인덱스 콤보를 사용하는 것이 가장 논리적 인 것 같습니다 :

names=[{}, {'name': 'Tom'},{'name': 'Tony'}]
names.index(filter(lambda n: n.get('name') == 'Tom', names)[0])
1

그리고 여러 경기가있을 수 있다고 생각하면 :

[names.index(n) for item in filter(lambda n: n.get('name') == 'Tom', names)]
[1]

2

@faham이 제공하는 답변은 멋진 한 줄짜리이지만 값을 포함하는 사전에 색인을 반환하지 않습니다. 대신 사전 자체를 반환합니다. 다음과 같은 간단한 방법이 있습니다. 둘 이상의 인덱스 목록이 하나 이상 있거나 비어있는 목록이없는 경우 :

list = [{'id':'1234','name':'Jason'},
        {'id':'2345','name':'Tom'},
        {'id':'3456','name':'Art'}]

[i for i, d in enumerate(list) if 'Tom' in d.values()]

산출:

>>> [1]

이 접근법에 대해 내가 좋아하는 것은 간단한 편집으로 인덱스와 사전의 목록을 튜플로 얻을 수 있다는 것입니다. 이것이 내가 해결 하고이 답변을 찾는 데 필요한 문제입니다. 다음에서는 다른 사전에 중복 값을 추가하여 작동 방식을 보여줍니다.

list = [{'id':'1234','name':'Jason'},
        {'id':'2345','name':'Tom'},
        {'id':'3456','name':'Art'},
        {'id':'4567','name':'Tom'}]

[(i, d) for i, d in enumerate(list) if 'Tom' in d.values()]

산출:

>>> [(1, {'id': '2345', 'name': 'Tom'}), (3, {'id': '4567', 'name': 'Tom'})]

이 솔루션은 값에 'Tom'이 포함 된 모든 사전을 찾습니다.


1

짧막 한 농담!?

elm = ([i for i in mylist if i['name'] == 'Tom'] or [None])[0]

0

주어진 iterable에 more_itertools.locate대해 술어를 만족시키는 항목의 위치를 ​​산출합니다.

import more_itertools as mit


iterable = [
    {"id": "1234", "name": "Jason"},
    {"id": "2345", "name": "Tom"},
    {"id": "3456", "name": "Art"}
]

list(mit.locate(iterable, pred=lambda d: d["name"] == "Tom"))
# [1]

more_itertools다른 유용한 도구 중에서 itertools 레시피 를 구현하는 타사 라이브러리입니다 .


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