एक चलने योग्य से पहली वस्तु प्राप्त करें जो एक शर्त से मेल खाती है


303

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

def first(the_iterable, condition = lambda x: True):
    for i in the_iterable:
        if condition(i):
            return i

इस फ़ंक्शन का उपयोग कुछ इस तरह किया जा सकता है:

>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4

हालाँकि, मैं ऐसा करने के लिए एक अच्छे अंतर्निर्मित / वन-लाइनर के बारे में नहीं सोच सकता। मैं विशेष रूप से इस समारोह की नकल नहीं करना चाहता अगर मुझे नहीं करना है। क्या किसी शर्त का मिलान करने वाली पहली वस्तु प्राप्त करने का एक अंतर्निहित तरीका है?


जवाबों:


475

पायथन 2.6 या नए में:

यदि आप चाहते हैं कि StopIterationअगर कोई मेल खाने वाला तत्व न मिले तो:

next(x for x in the_iterable if x > 3)

यदि आप चाहते हैं default_value(उदाहरण के लिए None) इसके बजाय लौटाया जाना चाहिए:

next((x for x in the_iterable if x > 3), default_value)

ध्यान दें कि आपको इस मामले में जनरेटर अभिव्यक्ति के चारों ओर कोष्ठक की एक अतिरिक्त जोड़ी की आवश्यकता है - जब भी जनरेटर अभिव्यक्ति एकमात्र तर्क नहीं हो, तो उनकी आवश्यकता होती है।

मुझे अधिकांश उत्तर nextबिल्ट-इन को अनदेखा करते हुए दिखाई देते हैं और इसलिए मुझे लगता है कि किसी रहस्यमय कारण से वे 100% 2.5 और पुराने संस्करणों पर ध्यान केंद्रित कर रहे हैं - पायथन-संस्करण के मुद्दे का उल्लेख किए बिना (लेकिन तब मैं उस उल्लेख का उल्लेख नहीं करता हूं जवाब है कि ऐसा उल्लेख nextमें निर्मित है, जिसके कारण मैंने सोचा कि यह आवश्यक एक जवाब अपने आप को प्रदान करने के लिए - कम से कम "सही संस्करण" मुद्दा रिकॉर्ड पर इस तरह से ;-) हो जाता है।

2.5 में, पुनरावृत्तियों की .next()विधि तुरंत उठती है StopIterationयदि पुनरावृत्ति तुरंत समाप्त हो जाती है - यानी, आपके उपयोग के मामले के लिए, यदि पुनरावृत्तियों में कोई भी वस्तु स्थिति को संतुष्ट नहीं करती है। यदि आप परवाह नहीं करते हैं (यानी, आप जानते हैं कि कम से कम एक संतोषजनक आइटम होना चाहिए) तो बस उपयोग करें .next()(एक जीनएक्सपी पर सबसे अच्छा, nextअंतर्निहित पायथन 2.6 और बेहतर के लिए लाइन )।

यदि आप देखभाल करते हैं, तो एक फ़ंक्शन में चीजों को लपेटना जैसा कि आपने पहली बार अपने क्यू में संकेत दिया था, सबसे अच्छा लगता है, और जबकि आपके द्वारा प्रस्तावित फ़ंक्शन कार्यान्वयन ठीक है, आप वैकल्पिक रूप से itertools, एक for...: breakलूप, या एक जीनएक्सपी, या try/except StopIterationफ़ंक्शन के शरीर के रूप में उपयोग कर सकते हैं। , जैसा कि विभिन्न उत्तर सुझाए गए हैं। इन विकल्पों में से किसी में भी बहुत अधिक मूल्य नहीं है, इसलिए मैं आपके द्वारा पहले प्रस्तावित स्टार्कली-सरल संस्करण के लिए जाऊंगा।


6
जैसा आप वर्णन करते हैं वैसा काम नहीं करता। यह StopIterationतब उठता है जब कोई तत्व नहीं पाया जाता है
सोर

चूंकि यह खोज परिणामों में आता है, इसलिए मैंने 2011 से @ सोर की टिप्पणी का अनुसरण किया है और चीजों को और अधिक स्पष्ट करने के लिए पहले पैराग्राफ को थोड़ा सा हटा दिया है। कृपया आगे बढ़ें और यदि आवश्यक हो तो मेरे संपादन में संशोधन करें।
कोस

4
चूंकि यह चयनित उत्तर है, इसलिए मुझे पहला तत्व सही ढंग से चुनने के लिए एक उत्तर साझा करने के लिए मजबूर महसूस होता है । संक्षेप में: अगले के उपयोग को प्रोत्साहित नहीं किया जाना चाहिए।
मसराद

1
@guyarad उस उत्तर में प्रस्तावित समाधान को किस तरह से "गुप्त" से कम उपयोग किया जाता है? अगले (उस उत्तर में) के खिलाफ एकमात्र तर्क यह है कि आपको एक अपवाद को संभालना होगा; वास्तव में ?
अब्राहम TS

मेरा दृष्टिकोण टिप्पणी लिखने के समय से थोड़ा अलग है। में तुम्हारी बात समझ रहा हूँ। यह कहा जा रहा है, संभालना StopIterationवास्तव में सुंदर नहीं है। बेहतर एक विधि का उपयोग करें।
मसराद

29

पुन: प्रयोज्य, प्रलेखित और परीक्षण किए गए फ़ंक्शन के रूप में

def first(iterable, condition = lambda x: True):
    """
    Returns the first item in the `iterable` that
    satisfies the `condition`.

    If the condition is not given, returns the first item of
    the iterable.

    Raises `StopIteration` if no item satysfing the condition is found.

    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)
    2
    >>> first(range(3, 100))
    3
    >>> first( () )
    Traceback (most recent call last):
    ...
    StopIteration
    """

    return next(x for x in iterable if condition(x))

डिफ़ॉल्ट तर्क के साथ संस्करण

@zorf ने इस फ़ंक्शन के एक संस्करण का सुझाव दिया, जहां आप पूर्वनिर्धारित वापसी मान रख सकते हैं यदि iterable खाली है या स्थिति से मेल खाने वाला कोई आइटम नहीं है:

def first(iterable, default = None, condition = lambda x: True):
    """
    Returns the first item in the `iterable` that
    satisfies the `condition`.

    If the condition is not given, returns the first item of
    the iterable.

    If the `default` argument is given and the iterable is empty,
    or if it has no items matching the condition, the `default` argument
    is returned if it matches the condition.

    The `default` argument being None is the same as it not being given.

    Raises `StopIteration` if no item satisfying the condition is found
    and default is not given or doesn't satisfy the condition.

    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)
    2
    >>> first(range(3, 100))
    3
    >>> first( () )
    Traceback (most recent call last):
    ...
    StopIteration
    >>> first([], default=1)
    1
    >>> first([], default=1, condition=lambda x: x % 2 == 0)
    Traceback (most recent call last):
    ...
    StopIteration
    >>> first([1,3,5], default=1, condition=lambda x: x % 2 == 0)
    Traceback (most recent call last):
    ...
    StopIteration
    """

    try:
        return next(x for x in iterable if condition(x))
    except StopIteration:
        if default is not None and condition(default):
            return default
        else:
            raise

6
यदि आप इसे एक विधि के साथ लपेट रहे हैं, तो कम से कम StopIteration को पकड़ें और EmptySequence त्रुटि को बढ़ाएं। जब कोई तत्व नहीं होंगे तो बहुत अधिक सुंदर होगा।
मसराद

@guyarad क्या एक तरह का ValueError है?
कारिडोरक

2
अजगर StopIterationमें @guyarad "तत्वों से बाहर" अपवाद है। मैं इसे फेंके जाने से कोई समस्या नहीं देखता। मैं शायद "कोई नहीं" के डिफ़ॉल्ट का उपयोग करूंगा जिसे फ़ंक्शन के डिफ़ॉल्ट पैरामीटर के रूप में पारित किया जा सकता है।
बाल्ड्रिक

1
बाल्डरिक मुझे लगता है कि यह एक पुनरावृत्ति विधि नहीं है। आप इसे एक पुनरावृति की प्रतियोगिता में नहीं बुलाएँगे। लेकिन मैं इसके बारे में बहुत दृढ़ता से महसूस नहीं कर रहा हूं :)
मसराद

1
एक वैकल्पिक डिफ़ॉल्ट तर्क होना चाहिए, और यदि उस तर्क की आपूर्ति नहीं की जाती है, तो केवल तब एक अपवाद बढ़ाएं जब अनुक्रम में कोई तत्व स्थिति को संतुष्ट नहीं करता है।
ज़ोर्फ

28

धिक्कार है अपवाद!

मुझे यह जवाब पसंद है । हालाँकि, जब कोई आइटम नहीं हैं, तो next()एक StopIterationअपवाद बढ़ाएं , मैं अपवाद से बचने के लिए निम्नलिखित स्निपेट का उपयोग करूंगा:

a = []
item = next((x for x in a), None)

उदाहरण के लिए,

a = []
item = next(x for x in a)

एक StopIterationअपवाद बढ़ाएगा ;

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

13

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

>>> (x for x in xrange(10) if x > 5).next()
6

किसी भी मामले में, आप शायद पकड़ना चाहते हैं StopIteration, अगर कोई तत्व आपकी स्थिति को संतुष्ट नहीं करता है।

तकनीकी रूप से, मुझे लगता है कि आप ऐसा कुछ कर सकते हैं:

>>> foo = None
>>> for foo in (x for x in xrange(10) if x > 5): break
... 
>>> foo
6

यह एक try/exceptब्लॉक बनाने से बचना होगा । लेकिन यह वाक्य रचना के लिए अस्पष्ट और अपमानजनक लगता है।


+1: न अस्पष्ट, न ही अपमानजनक। सभी बातों पर विचार किया, आखिरी बहुत साफ लगता है।
एस.लॉट

6
अंतिम एक बिल्कुल भी साफ नहीं है - काम को स्पष्ट किए बिना for foo in genex: breakसिर्फ एक तरीका है foo = next(genex)और यदि ऑपरेशन को समझ में नहीं आता है तो अपवाद के साथ उठाया जाएगा। अपवाद को पकड़ने के बजाय विफलता कोड के साथ समाप्त करना आमतौर पर पायथन में एक बुरी बात है।
माइक ग्राहम

13

पायथन 3 में सबसे कुशल तरीका निम्नलिखित में से एक है (एक समान उदाहरण का उपयोग करके):

साथ "समझ" शैली:

next(i for i in range(100000000) if i == 1000)

चेतावनी : अभिव्यक्ति पायथन 2 के साथ भी काम करती है, लेकिन उदाहरण में इसका उपयोग किया जाता rangeहै, जो पायथन 2 जैसी सूची के बजाय पायथन 3 में एक पुन: उपयोग योग्य वस्तु देता है (यदि आप पायथन 2 में पुनरावृत्त बनाना चाहते हैं)xrange )।

ध्यान दें कि अभिव्यक्ति को समझने की अभिव्यक्ति में एक सूची बनाने से बचें next([i for ...]), जो तत्वों को फ़िल्टर करने से पहले सभी तत्वों के साथ एक सूची बनाने का कारण होगा, और पुनरावृति को रोकने के बजाय पूरे विकल्पों को संसाधित करने का कारण होगा।i == 1000

साथ "कार्यात्मक" शैली:

next(filter(lambda i: i == 1000, range(100000000)))

चेतावनी : यह और भी जगह, अजगर 2 में नहीं काम करता है rangeके साथ xrangeकि वजह से filterएक iterator (अक्षम) के बजाय एक सूची बनाते हैं, औरnext समारोह केवल iterators के साथ काम करता है।

डिफ़ॉल्ट मान

जैसा कि अन्य प्रतिक्रियाओं में उल्लेख किया गया है, आपको फ़ंक्शन में एक अतिरिक्त-पैरामीटर जोड़ना होगा nextयदि आप शर्त पूरी नहीं होने पर उठाए गए अपवाद से बचना चाहते हैं।

"कार्यात्मक" शैली:

next(filter(lambda i: i == 1000, range(100000000)), False)

"समझना" शैली:

इस शैली के साथ आप को समझने के लिए समझने की जरूरत ()है SyntaxError: Generator expression must be parenthesized if not sole argument:

next((i for i in range(100000000) if i == 1000), False)


6

itertoolsमॉड्यूल iterators के लिए एक फिल्टर समारोह में शामिल है। फ़िल्टर्ड इटरेटर का पहला तत्व उस next()पर कॉल करके प्राप्त किया जा सकता है:

from itertools import ifilter

print ifilter((lambda i: i > 3), range(10)).next()

2
जेनरेटर के भाव सरल हैं।
एरिक ओ लेबिगॉट

1
( i) filterऔर ( i) mapउन मामलों के लिए समझ में आता है जहां पहले से लागू किए जा रहे कार्य मौजूद हैं, लेकिन इस तरह की स्थिति में यह बहुत अधिक समझ में आता है बस एक जनरेटर अभिव्यक्ति का उपयोग करने के लिए।
माइक ग्राहम

यह सबसे अच्छा जवाब है। सूची की समझ से बचें xahlee.info/comp/list_comprehension.html
mit

6

पायथन के पुराने संस्करणों के लिए जहां अगला बिल्ट-इन मौजूद नहीं है:

(x for x in range(10) if x > 3).next()

5

का उपयोग करके

(index for index, value in enumerate(the_iterable) if condition(value))

कोई पहली वस्तु के मूल्य की स्थिति को in_iterable में जांच सकता है , और in_iterable में सभी वस्तुओं का मूल्यांकन करने की आवश्यकता के बिना अपना सूचकांक प्राप्त कर सकता है

उपयोग करने के लिए पूर्ण अभिव्यक्ति है

first_index = next(index for index, value in enumerate(the_iterable) if condition(value))

यहाँ first_index ऊपर चर्चा की गई अभिव्यक्ति में पहचाने गए पहले मान के मान को मानता है।


4

इस सवाल के पहले से ही शानदार जवाब हैं। मैं केवल अपने दो सेंट जोड़ रहा हूं क्योंकि मैं यहां अपनी समस्या का हल खोजने की कोशिश कर रहा हूं, जो ओपी के समान है।

यदि आप जनरेटर का उपयोग करते हुए मापदंड से मेल खाते पहले आइटम का INDEX ढूंढना चाहते हैं, तो आप बस यह कर सकते हैं:

next(index for index, value in enumerate(iterable) if condition)

इसे भी देखें: stackoverflow.com/questions/1701211/…
dreftymac

0

आप भी उपयोग कर सकते हैं argwhere Numpy में फ़ंक्शन का । उदाहरण के लिए:

i) "helloworld" में पहला "l" ढूंढें:

import numpy as np
l = list("helloworld") # Create list
i = np.argwhere(np.array(l)=="l") # i = array([[2],[3],[8]])
index_of_first = i.min()

ii) पहला यादृच्छिक संख्या ज्ञात करें> 0.1

import numpy as np
r = np.random.rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_first = i.min()

iii) अंतिम यादृच्छिक संख्या> 0.1 ज्ञात करें

import numpy as np
r = np.random.rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_last = i.max()

-1

पायथन 3 में:

a = (None, False, 0, 1)
assert next(filter(None, a)) == 1

पायथन 2.6 में:

a = (None, False, 0, 1)
assert next(iter(filter(None, a))) == 1

संपादित करें: मैंने सोचा था कि यह स्पष्ट था, लेकिन स्पष्ट रूप से नहीं: इसके बजाय Noneआप lambdaशर्त के लिए एक चेक (या ए ) पास कर सकते हैं:

a = [2,3,4,5,6,7,8]
assert next(filter(lambda x: x%2, a)) == 3

-3

एक लाइन:

thefirst = [i for i in range(10) if i > 3][0]

यदि आप यह सुनिश्चित नहीं करते हैं कि कोई भी तत्व मानदंड के अनुसार मान्य होगा, तो आपको इस के साथ संलग्न करना चाहिए try/exceptक्योंकि यह [0]एक बढ़ा सकता है IndexError


TypeError: 'जनरेटर' ऑब्जेक्ट unsubscriptable है
जोश ली

मेरी खराब, सूची की समझ होनी चाहिए न कि एक जनरेटर, फिक्स्ड ... धन्यवाद! :)
मिज़िपज़ोर

2
पूरे चलने योग्य का मूल्यांकन करने का कोई कारण नहीं है (जो संभव नहीं हो सकता है)। प्रदान किए गए अन्य समाधानों में से एक का उपयोग करना अधिक मजबूत और कुशल है।
माइक ग्राहम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.