शब्दकोश खोज की पायथन सूची


449

मुझे लगता है कि यह है:

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

और "पाम" नाम के रूप में खोज कर, मैं संबंधित शब्दकोश को पुनः प्राप्त करना चाहता हूं: {name: "Pam", age: 7}

इसे कैसे प्राप्त किया जाए?

जवाबों:


512

आप एक जनरेटर अभिव्यक्ति का उपयोग कर सकते हैं :

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

यदि आपको उस आइटम को संभालने की आवश्यकता नहीं है जो वहां नहीं है, तो आप वह कर सकते हैं जो उपयोगकर्ता मैट ने अपनी टिप्पणी में सुझाया है और थोड़ा अलग उपयोग करके डिफ़ॉल्ट प्रदान करें:

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

और आइटम के सूचकांक को खोजने के लिए, आइटम के बजाय, आप सूची को गणना () कर सकते हैं :

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

230
बस किसी और को थोड़ा समय बचाने के लिए, अगर आपको सूची में डिफ़ॉल्ट मान की आवश्यकता है "पाम" सूची में बस नहीं है: अगला (आइटम में आइटम के लिए आइटम यदि आइटम ["नाम"] == "पाम") , कोई नहीं)
मैट

1
किस बारे में [item for item in dicts if item["name"] == "Pam"][0]?
रॉबर्ट ऑक्ट

3
@Moberg, यह अभी भी एक सूची समझ है, इसलिए यह मिलान आइटम की स्थिति की परवाह किए बिना पूरे इनपुट अनुक्रम पर पुनरावृत्ति करेगा।
फ्रैडरिक हमीदी

7
यह रोक त्रुटि को बढ़ाएगा यदि कुंजी शब्दकोश में मौजूद नहीं है
किशन

3
@Siemkowski: फिर enumerate()एक रनिंग इंडेक्स जेनरेट करने के लिए जोड़ें next(i for i, item in enumerate(dicts) if item["name"] == "Pam"):।
मार्टिन पीटर्स

218

यह मुझे सबसे अधिक आकर्षक लगता है:

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

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

परिणाम (पायथन 2 में एक सूची के रूप में लौटा):

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

नोट: पायथन 3 में, एक फ़िल्टर ऑब्जेक्ट वापस आ गया है। तो python3 समाधान होगा:

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

14
गौर करने वाली बात है कि यह उत्तर लोगों में 'पाम' के लिए सभी मैचों के साथ एक सूची देता है, वैकल्पिक रूप से हम उन सभी लोगों की सूची प्राप्त कर सकते हैं जो तुलना ऑपरेटर को बदलकर 'पाम' नहीं हैं! =! +1
ओमेगा

2
यह भी ध्यान देने योग्य है कि परिणाम एक फ़िल्टर ऑब्जेक्ट है, न कि एक सूची - यदि आप चीजों का उपयोग करना चाहते हैं len(), तो आपको list()पहले परिणाम पर कॉल करना होगा। या: stackoverflow.com/questions/19182188/…
wasabigeek

@wasabigeek यही मेरा पायथन 2.7 कहता है: लोग = [{'नाम': "टॉम", 'उम्र': 10}, {'नाम': "मार्क", 'उम्र': 5}, {'नाम': "पैम", 'उम्र': 7}] आर = फिल्टर (लंबोदर व्यक्ति: व्यक्ति ['नाम'] == 'पाम', लोग) प्रकार (आर) सूची तो rएक हैlist
पाओल्क

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 का जवाब बहुत अच्छा है। पाइथन में 3. .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
यह पायथन 3.x के लिए सबसे अच्छा उत्तर है। आप dicts से किसी विशिष्ट तत्व की जरूरत है, उम्र की तरह, आप लिख सकते हैं: अगले ((item.get (dicts में वस्तु के लिए 'उम्र') यदि आइटम [ "नाम"] == "पाम"), झूठी)
cwhisperer

47

आप एक सूची समझ का उपयोग कर सकते हैं :

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

4
यह अच्छा है क्योंकि यह एक से अधिक होने पर सभी मैच लौटाता है। नहीं बिल्कुल क्या सवाल के लिए पूछा, लेकिन यह है कि मैं क्या जरूरत है! धन्यवाद!
user3303554

नोट यह भी एक सूची देता है!
अब्बास

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

यह दिए गए नाम के साथ सूची में पहला शब्दकोश लौटाएगा।
रिकी रॉबिन्सन

5
बस इस बहुत उपयोगी दिनचर्या को थोड़ा और सामान्य बनाने के लिए:def search(list, key, value): for item in list: if item[key] == value: return item
जैक जेम्स

30

मैंने शब्दकोशों की एक सूची के माध्यम से जाने और उन शब्दकोशों को वापस करने के लिए विभिन्न तरीकों का परीक्षण किया जहां कुंजी x का एक निश्चित मूल्य है।

परिणाम:

  • गति: सूची समझ> जनरेटर अभिव्यक्ति >> सामान्य सूची पुनरावृत्ति >>> फ़िल्टर।
  • सूची में dicts की संख्या के साथ सभी पैमाने रैखिक (10x सूची आकार -> 10x समय)।
  • प्रति शब्दकोष की चाबियों की बड़ी मात्रा (हजारों) के लिए गति को काफी प्रभावित नहीं करती है। कृपया इस ग्राफ को मैंने देखें: https://imgur.com/a/quQzv (विधि नाम नीचे देखें)।

पायथन 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

मैंने फंक्शन z () जोड़ा, जो कि ऊपर दिए गए Frédéric Hamidi द्वारा बताया गया है। यहां Py प्रोफ़ाइल से परिणाम दिए गए हैं।
लेऑन

10

@ Frédéric Hamidi में बस थोड़ा सा जोड़ने के लिए।

मामले में आप सुनिश्चित नहीं हैं कि एक कुंजी dicts की सूची में है, कुछ इस तरह से मदद करेगा:

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

या बसitem.get("name") == "Pam"
एंड्रियास हैफेरबर्ग

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
मैं सुझाव दे सकता हूं कि अगर नाम में d।
जिम डेनिस

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अगर हम खोज रहे हैं तो नाम वापस नहीं मिलेगा ।


यह सूची की समझ से बहुत धीमी है।
अनुपमचूघ

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]

वास्तव में आप एक नाम के साथ एक शब्दकोश हो सकते हैं = इसमें कोई भी वस्तु नहीं; लेकिन यह वास्तव में इस सूची की समझ के साथ काम नहीं करेगा और संभवतः यह आपके डेटा स्टोर में अनुमति देने के लिए समझदार नहीं है।
जिम डेनिस

1
यदि डिबग मोड बंद है, तो मुखर को छोड़ दिया जा सकता है।
ब्लॅकफिस्क

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

यहाँ थ्रॉहग सूची का उपयोग करते हुए एक तुलना की जाती है, फ़िल्टर + लैम्ब्डा या रीफैक्टरिंग (यदि आपके मामले में आवश्यक या वैध है) का उपयोग करते हुए आपका कोड डिक्सेस की सूची के बजाय डिक्ट्स के हुक्म के लिए है

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

निष्कर्ष: स्पष्ट रूप से डिक्सेस का शब्दकोश होना उन मामलों में खोज करने में सक्षम होने के लिए सबसे कुशल तरीका है, जहां आप जानते हैं कि आप केवल आईडी द्वारा खोज रहे होंगे। दिलचस्प रूप से फ़िल्टर का उपयोग सबसे धीमा समाधान है।


0

आपको सूची के सभी तत्वों से गुजरना होगा। कोई शॉर्टकट नहीं है!

जब तक कहीं और आप सूची के आइटम की ओर इशारा करते हुए नामों का एक शब्दकोश रखते हैं, लेकिन तब आपको अपनी सूची से एक तत्व को पॉप करने के परिणामों का ध्यान रखना होगा।


एक अनसुलझी सूची और एक लापता कुंजी के मामले में यह कथन सही है, लेकिन सामान्य रूप से नहीं। यदि सूची को क्रमबद्ध करने के लिए जाना जाता है, तो सभी तत्वों को पुनरावृत्त होने की आवश्यकता नहीं है। इसके अलावा, यदि कोई एकल रिकॉर्ड हिट है और आप जानते हैं कि कुंजियाँ अद्वितीय हैं या केवल एक तत्व की आवश्यकता है, तो पुनरावृत्ति एकल आइटम के साथ वापस आ सकती है।
user25064 19

@ user334856 का जवाब देखें
Melih Yıldız

@ MelihYıldız 'शायद मैं अपने बयान में स्पष्ट नहीं था। उत्तर में stackoverflow.com/a/8653572/512225 की एक सूची समझने वाले user334856 का उपयोग करके पूरी सूची से गुजर रहा है। यह मेरे कथन की पुष्टि करता है। आपके द्वारा दिया गया उत्तर, यह कहने का एक और तरीका है कि मैंने क्या लिखा है।
जिमीफिकि

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

0

यहां प्रस्तावित अधिकांश (यदि सभी नहीं) कार्यान्वयन में दो दोष हैं:

  • वे खोज के लिए पारित होने के लिए केवल एक कुंजी मानते हैं, जबकि जटिल तानाशाही के लिए यह अधिक दिलचस्प हो सकता है
  • वे मान लेते हैं कि खोज के लिए पास की गई सभी चाबियां डक्ट्स में मौजूद हैं, इसलिए जब वे नहीं होते हैं, तो वे 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)

शायद सबसे अधिक पायथोनिक नहीं है, लेकिन कम से कम थोड़ा और अधिक असफल।

उपयोग:

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

गित

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.