397

What would be the most elegant and efficient way of finding/returning the first list item that matches a certain criterion?

For example, if I have a list of objects and I would like to get the first object of those with attribute obj.val==5. I could of course use list comprehension, but that would incur O(n) and if n is large, it's wasteful. I could also use a loop with break once the criterion was met, but I thought there could be a more pythonic/elegant solution.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Jonathan Livni
  • 101,334
  • 104
  • 266
  • 359

2 Answers2

737

If you don't have any other indexes or sorted information for your objects, then you will have to iterate until such an object is found:

next(obj for obj in objs if obj.val == 5)

This is however faster than a complete list comprehension. Compare these two:

[i for i in xrange(100000) if i == 1000][0]

next(i for i in xrange(100000) if i == 1000)

The first one needs 5.75ms, the second one 58.3µs (100 times faster because the loop 100 times shorter).

Andrii Abramov
  • 10,019
  • 9
  • 74
  • 96
eumiro
  • 207,213
  • 34
  • 299
  • 261
  • 191
    `next` also provides a `default` argument, in the case that no object exists. E.g. `next((i for i in range(500) if i > 600), 600)` will return 600. – Casey Kuball Jul 24 '12 at 23:13
  • 31
    Python [**`next()`**](http://docs.python.org/2/library/functions.html#next) – Jonathon Reinhart Oct 19 '13 at 04:29
  • 13
    Well, this is it, but I just expected the right answer to look cooler. We always advertise python for being so elegant. If you want it to be robust, you should provide `default` (e.g. `None`) - and then you need to not forget that `Generator expression must be parenthesized if not sole argument`... Well, how does this affect readability? E.g. first non-path arugment: `next((arg for arg in sys.argv if not os.path.exists(arg)), None)` - not very friendly. – Tomasz Gandor Mar 18 '16 at 11:38
  • 6
    True. If it bugs you enough you could always do `def first(items, pred): return next((i for i in items if pred(i)), None)` – gladed Mar 25 '16 at 23:10
  • 2
    what if you want to find the item and the index too? – Charlie Parker Jul 10 '16 at 20:04
  • 1
    @CharlieParker use `enumerate`: `next((i for i in enumerate(items) if pred(i[1])), None)`. – Hubert Kario Nov 08 '16 at 15:17
  • Oh, I was soooo hoping this was in the standard library. One more reason to love python! – dusktreader Jan 04 '17 at 23:52
  • 2
    an alternate enumerate version: `next(((i, v) for (i, v) in enumerate(items) if pred(v)), None)` Works exactly the same, but shows the index and value from `enumerate` (yes, we all know how it works, but it doesn't hurt) and lets the predicate work on the value without indexing it. – Baldrickk May 24 '18 at 07:46
  • @gladed What is `pred` ?I got pred is not defined error – West Sep 11 '20 at 00:56
  • `pred` is short for predicate, passed into `first()`, and would be a lambda that returns True or False for any i. But I think just using `next()` inline is more pythonic. – gladed Sep 11 '20 at 15:18
1

This will return the object if found, else it will return "not found"

a = [100, 200, 300, 400, 500]

def search(b):
    try:
        k = a.index(b)
        return a[k] 
    except ValueError:
        return 'not found'

print(search(500))
Neuron
  • 5,141
  • 5
  • 38
  • 59
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • 2
    This is nice, but works only when the criteria is a comparison to list item. I was looking for a more generic solution to handle a wider scope of selection – Jonathan Livni Mar 26 '12 at 09:38
  • but @Jonathan in your question you mentioned **efficient way of finding\returning the first list item** so the above list a=[100,200,300,400,500] can contain any type of object not just numbers. – Ashwini Chaudhary Mar 26 '12 at 09:42
  • 4
    and I conclude the sentence with "...that matches a certain criteria", which is not the same as to match on equality or identity :) I did +1 as I think your solution is good for equality\identity private case – Jonathan Livni Mar 26 '12 at 11:35
  • 1
    returning a string in case of an error is not a clean solution. for this very case exceptions exist. Also, what if "not found" is actually part of the list? – Neuron Feb 07 '23 at 09:45