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.