एक सूची के भीतर एक तानाशाह के सूचकांक का पता लगाएं, तानाशाह के मूल्य का मिलान करके


130

मेरे पास सूची की एक सूची है:

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

मैं कुशलता से सूचकांक स्थिति [0], [1], या [2] नाम = 'टॉम' पर मेल करके कैसे पा सकता हूँ?

यदि यह एक-आयामी सूची थी तो मैं list.index () कर सकता था, लेकिन मुझे यकीन नहीं है कि सूची के भीतर dicts के मूल्यों को खोजकर कैसे आगे बढ़ना है।


6
"सूची" सूची निर्माणकर्ता है, आप सूची के लिए एक और नाम भी बेहतर चुनते हैं (एक उदाहरण में भी)। और अगर कोई तत्व नहीं मिला तो क्या प्रतिक्रिया होनी चाहिए? एक अपवाद उठाएं? कोई नहीं लौटा?
20

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

यदि आपको नाम से बार-बार लाने की आवश्यकता है, तो आपको उन्हें नाम से (एक शब्दकोश का उपयोग करके) अनुक्रमित करना चाहिए, इस तरह से संचालन ओ (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 यह उतना पठनीय नहीं है या पाइथोनिक @ एमिल का जवाब है। क्योंकि इरादा वास्तव में एक जनरेटर बनाने के लिए नहीं है (और इसके लिए उपयोग next()करना मुझे अजीब लगता है), उद्देश्य सिर्फ सूचकांक प्राप्त करना है। इसके अलावा, यह StopIteration को बढ़ाता है, जबकि पायथन lst.index()विधि ValueError को बढ़ाती है।
बेन होयट

@benhoyt: मुझे स्टॉपइंटरेशन अपवाद या तो पसंद नहीं है, लेकिन जब आप अगले () के डिफ़ॉल्ट मान को बदल सकते हैं, तो वह जो अपवाद उठाता है वह तय है। Pythonicity कुछ व्यक्तिपरक है इसलिए मैं इसे विवाद नहीं करूंगा, शायद एक for-loop अधिक pythonic है। दूसरी ओर, कुछ लोग अगले (पहले) के लिए उपनाम (), और निश्चित रूप से बेहतर लगता है: पहला (सूचकांक के लिए (सूचकांक, डी) में ...)।
21

first()बेहतर ध्वनि करता है। आप हमेशा StopIteration को छोड़कर / कोशिश कर सकते हैं और ValueError बढ़ा सकते हैं ताकि कॉलर में स्थिरता हो। वैकल्पिक next()रूप से डिफ़ॉल्ट -1 पर सेट करें ।
बेन होयट

1
@ gdw2: मुझे SyntaxError: Generator expression must be parenthesized if not sole argumentऐसा करते समय मिलता है।
अवलिवा

2
@avoliva अगले की तरह एक कोष्ठक जोड़ते हैंnext((index for (index, d) in enumerate(lst) if d["name"] == "Tom"), None)
हुसैनक

45

एक सरल पठनीय संस्करण है

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

8
यह सबसे पठनीय और पायथोनिक लगता है। यह str.find()अच्छी तरह से व्यवहार की नकल भी करता है । आप इसे कॉल भी कर सकते हैं index()और ValueErrorयदि रिटर्न बेहतर हो तो रिटर्न -1 के बजाय बढ़ा सकते हैं ।
बेन होयट

6
सहमत - जब कोई मैच नहीं मिलता है, तो वापसी करते हुए, आपको हमेशा सूची में अंतिम तानाशाही मिलेगी, जो शायद आप नहीं चाहते हैं। कॉलिंग कोड में किसी भी मैच की वापसी के लिए बेहतर है कि किसी मैच की जांच की जाए।
शाकर

9

यह कुशल नहीं होगा, क्योंकि आपको इसमें प्रत्येक आइटम (O (n)) की जाँच करने वाली सूची को चलना होगा। यदि आप दक्षता चाहते हैं, तो आप डिक्टेट्स का उपयोग कर सकते हैं । इस सवाल पर, यहां इसे खोजने का एक संभावित तरीका है (हालांकि, यदि आप इस डेटा संरचना से चिपके रहना चाहते हैं, तो यह वास्तव में जनरेटर का उपयोग करने के लिए अधिक कुशल है जैसा कि ब्रेंट न्यूए ने टिप्पणियों में लिखा है; यह भी देखें टॉस्कलैंड का जवाब):

>>> 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
आप एक जनरेटर का उपयोग करके अपनी इच्छानुसार दक्षता हासिल कर सकते हैं। देखिए टोकन का जवाब।
ब्रेंट न्यू डे

2
@Bent Newey: जनरेटर इस तथ्य को नहीं बदलता है, कि आपको पूरी सूची को पार करना होगा, खोज O (n) बनाने के लिए aeter का दावा है ... उस सूची के आधार पर, जनरेटर का उपयोग करने के बीच का अंतर बनाम उपयोग कर रहा है लूप के लिए या जो भी नगण्य हो सकता है, एक सूची का उपयोग करके बनाम के बीच के अंतर को भांप सकता है, सूची का उपयोग नहीं कर सकता है
डिर्क

@ बेंट: आप सही हैं, लेकिन क्या यह एक डिक्शनरी में ओ (1) लुक को हरा सकता है, यदि सूची के अंत में खोजा गया आइटम है तो?
ऐक्टर

1
@Dirk एक मिलान मिलने पर जनरेटर पर अगला () कॉल बंद हो जाता है, इसलिए इसे पूरी सूची को पार नहीं करना पड़ता है।
ब्रेंट न्यू डे

@aeter आप एक उचित बिंदु बनाते हैं। मैं एक मैच मिलने पर रोकने में सक्षम होने की बात कर रहा था।
ब्रेंट न्यू डे

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

@ फहम द्वारा प्रस्तुत उत्तर एक अच्छा एक लाइनर है, लेकिन यह मूल्य वाले शब्दकोश में सूचकांक वापस नहीं करता है। इसके बजाय यह शब्दकोश को ही लौटाता है। यहाँ प्राप्त करने का एक सरल तरीका है: यदि एक से अधिक हैं, या एक खाली सूची की सूची है, तो एक या एक से अधिक सूचीएँ हैं:

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

यह समाधान उनके सभी मूल्यों में 'टॉम' वाले सभी शब्दकोशों को ढूंढता है।


1

एक लाइन!?

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

0

किसी दिए गए पुनरावृत्ति के लिए, 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एक तृतीय-पक्ष लाइब्रेरी है जो अन्य उपयोगी उपकरणों के बीच इटर्टूलस व्यंजनों को लागू करती है


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