Python sözlük arama listesi


450

Varsayalım:

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

ve "Pam" adını arayarak, ilgili sözlüğü almak istiyorum: {name: "Pam", age: 7}

Bunu nasıl başarabilirim?

Yanıtlar:


514

Bir jeneratör ifadesi kullanabilirsiniz :

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

Öğenin orada bulunmamasıyla ilgilenmeniz gerekiyorsa, Matt'in yorumunda önerdiği kullanıcıyı yapabilir ve biraz farklı bir API kullanarak bir varsayılan sağlayabilirsiniz:

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

Ve öğenin kendisini değil, öğenin dizinini bulmak için listeyi numaralandırabilir () :

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

230
Sadece biraz zaman kazanmak için, "Pam" etkinliğinde varsayılan bir değere ihtiyacınız varsa listede yok: next ((item ["name"] == "Pam" ise dikte içindeki öğe için öğe) , Hiçbiri)
Matt

1
Ne olmuş [item for item in dicts if item["name"] == "Pam"][0]?
Moberg

3
@Moberg, bu hala bir liste kavrama, bu yüzden eşleşen öğenin konumundan bağımsız olarak tüm giriş dizisi üzerinde tekrarlayacaktır.
Frédéric Hamidi

7
Bu, sözlükte anahtar yoksa stopiteration hatasını artıracaktır
Kishan

3
@Siemkowski: o zaman eklemek enumerate()çalışan bir indeks oluşturmak için: next(i for i, item in enumerate(dicts) if item["name"] == "Pam").
Martijn Pieters

218

Bu bana en pitonik bir şekilde bakıyor:

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

filter(lambda person: person['name'] == 'Pam', people)

sonuç (Python 2'de liste olarak döndürülür):

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

Not: Python 3'te bir filtre nesnesi döndürülür. Yani python3 çözümü:

list(filter(lambda person: person['name'] == 'Pam', people))

14
Bu cevabın insanlarda 'Pam' için tüm eşleşmeleri içeren bir liste döndürdüğünü belirtmek gerekir, alternatif olarak karşılaştırma operatörünü! = Olarak değiştirerek 'Pam' olmayan tüm kişilerin bir listesini alabiliriz. +1
Onema

2
Ayrıca, sonucun bir liste değil, bir filtre nesnesi olduğunu belirtmeye değer - eğer gibi şeyleri kullanmak len()istiyorsanız list(), önce sonucu çağırmanız gerekir . Veya: stackoverflow.com/questions/19182188/…
wasabigeek

@wasabigeek Python 2.7'm şöyle diyor: people = [{'name': "Tom", 'age': 10}, {'name': "Mark", 'age': 5}, {'name': "PAM", 'yaş': 7}] r = filtre (lamda kişi: kullanıcı [ 'adı'] == 'PAM', insanlar) tipi (r) liste Yani ra,list
PaoloC

1
Liste kavrayışları, harita / filtre / azaltma
işleminden

2
İlk maçı alın:next(filter(lambda x: x['name'] == 'Pam', dicts))
xgMz

60

@ Frédéric Hamidi'nin cevabı harika. Python 3.x'te sözdizimi .next()biraz değişti. Böylece küçük bir değişiklik:

>>> 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 tarafından yapılan yorumlarda belirtildiği gibi, varsayılan bir değer ekleyebilirsiniz:

>>> 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
Bu Python 3.x için en iyi cevaptır. Yaş gibi diktatörlerden belirli bir öğeye ihtiyacınız varsa şunu yazabilirsiniz: next [(item.get ('age'), eğer öğe ["name"] == "Pam"), False)
cwhisperer

47

Bir liste kavrayışı kullanabilirsiniz :

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

4
Birden fazla varsa tüm eşleşmeleri döndürdüğü için bu iyidir. Sorunun tam olarak ne istediğini değil, ama ihtiyacım olan şey bu! Teşekkürler!
user3303554

Bunun bir liste döndürdüğünü de unutmayın!
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")

Listede verilen ada sahip ilk sözlüğü döndürür.
Ricky Robinson

5
Sadece bu çok kullanışlı rutini biraz daha genel yapmak için:def search(list, key, value): for item in list: if item[key] == value: return item
Jack James

30

Sözlükler listesini gözden geçirmek ve x anahtarının belirli bir değere sahip olduğu sözlükleri döndürmek için çeşitli yöntemleri test ettim.

Sonuçlar:

  • Hız: liste anlama> jeneratör ifadesi >> normal liste tekrarı >>> filtre.
  • Tüm ölçekler listedeki dikte sayısı ile doğrusal (10x liste boyutu -> 10x zaman).
  • Sözlük başına tuşlar büyük miktarlarda (binlerce) tuşların hızını önemli ölçüde etkilemez. Lütfen hesapladığım bu grafiğe bakın: https://imgur.com/a/quQzv (yöntem adları aşağıya bakın).

Tüm testler Python 3.6 .4, W7x64 ile yapılmıştır.

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

Sonuçlar:

1.7303 # normal list iteration 
1.3849 # generator expression 
1.3158 # list comprehension 
7.7848 # filter

Yukarıda Frédéric Hamidi'nin işaret ettiği gibi uygulayan z () fonksiyonunu ekledim. İşte Py profilinden sonuçlar.
leon

10

@ FrédéricHamidi'ye sadece küçük bir parça eklemek için.

Bir anahtarın dikteler listesinde olduğundan emin değilseniz, bunun gibi bir şey yardımcı olacaktır:

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

ya da sadeceitem.get("name") == "Pam"
Andreas Haferburg

10

Panda paketini hiç denediniz mi? Bu tür bir arama görevi için mükemmeldir ve optimize edilmiştir.

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

Pandaların daha hızlı çalışma sürelerini 100k + girişlerle daha hızlı bir şekilde göstermek için aşağıya biraz kıyaslama ekledim:

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

Bu, sözlükler listesindeki bir değeri aramanın genel bir yoludur:

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]

Bu bir yol ...


1
Eğer d.get ('name', '') == 'Pam'] ... isimlerinde x için "d" ismini "isim" anahtarına sahip olmayan "isim" lerde herhangi bir girişi işlemek için önerebilirim.
Jim Dennis

6

Sadece liste kavrayışı kullanarak:

[i for i in dct if i['name'] == 'Pam'][0]

Basit kod:

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

Bunu Python'da filtre ve sonraki yöntemlerle yapabilirsiniz.

filteryöntemi verilen diziyi filtreler ve bir yineleyici döndürür. nextyöntemi bir yineleyici kabul eder ve listedeki bir sonraki öğeyi döndürür.

Böylece elementi şu şekilde bulabilirsiniz:

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)

ve çıktı,

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

Not: NoneAradığınız kod bulunamazsa, yukarıdaki kod olayla sonuçlanır.


Bu liste kavrayışlarından çok daha yavaştır.
AnupamChugh

4

İlk düşüncem, bu sözlüklerden bir sözlük oluşturmayı düşünmek isteyebileceğinizdir ... örneğin, eğer az sayıda kez aradıysanız.

Ancak bu erken bir optimizasyon olabilir. Sorun ne olabilir:

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]

Aslında içinde bir isim = Yok öğe olan bir sözlük olabilir; ancak bu, bu liste kavrayışı ile gerçekten işe yaramaz ve muhtemelen veri deponuzda buna izin vermek mantıklı değildir.
Jim Dennis

1
hata ayıklama modu kapalı olduğunda uyarılar atlanabilir.
bluppfisk

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

Liste kavrayışlarını kullanmanın basit bir yolu l, liste

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

sonra

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

2

Bunu deneyebilirsiniz:

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

Burada, yineleme listesi yerine dikteleri dikte etmek için filtre + lambda veya yeniden kodlama (durumunuz için geçerliyse veya durumunuz için geçerliyse) kullanarak yinelenen throuhg listesini kullanan bir karşılaştırma

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

Ve çıktı şudur:

List of Dictionries took: 0.0099310874939
Using filter took: 0.0121960639954
Dict of Dicts took: 4.05311584473e-06

Sonuç: Açıkçası bir dikte sözlüğüne sahip olmak, sadece kimliklerle arama yapacağınızı bildiğiniz bu durumlarda arama yapmanın en etkili yoludur. ilginç bir şekilde filtre kullanmak en yavaş çözümdür.


1

Burada önerilen uygulamaların çoğunun (hepsi olmasa da) iki kusuru vardır:

  • Arama için yalnızca bir anahtarın geçileceğini varsayarken, karmaşık dikte için daha fazlasına sahip olmak ilginç olabilir
  • Arama için geçirilen tüm anahtarların dikte bulunduğunu varsayarlar, bu nedenle KeyError'ın olmadığı zamanlarda meydana gelmesi ile doğru bir şekilde ilgilenmezler.

Güncellenmiş bir teklif:

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)

Belki en pitonik değil, en azından biraz daha güvenli.

Kullanımı:

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

Özü .


0

Listenin tüm unsurlarını gözden geçirmelisiniz. Kısayol yok!

Başka bir yerde, listenin öğelerini işaret eden adların sözlüğünü tutmazsanız, ancak listenizden bir öğeyi açmanın sonuçlarına dikkat etmeniz gerekir.


Sıralanmamış bir liste ve eksik anahtar durumunda bu ifade doğrudur, ancak genel olarak değildir. Listenin sıralandığı biliniyorsa, tüm öğelerin tekrarlanması gerekmez. Ayrıca, tek bir kayıt vurulursa ve tuşların benzersiz olduğunu veya yalnızca bir öğe gerektirdiğini biliyorsanız, yineleme döndürülen tek öğe ile durdurulabilir.
user25064

@ user334856
Melih Yıldız

@ MelihYıldız 'belki ifademde net değildim. Yanıttaki user334856 bir liste anlama kullanarak stackoverflow.com/a/8653572/512225 tüm liste boyunca gidiyor. Bu benim beyanımı teyit ediyor. Bahsettiğiniz cevap, yazdıklarımı söylemenin başka bir yoludur.
jimifiki

0

Aynı soruya bir cevap ararken bu konuyu buldum. Geç bir cevap olduğunu fark etsem de, başkalarına faydalı olması durumunda katkıda bulunacağımı düşündüm:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.