407

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.

2
  • 4
    what if you want to get the item and the index? Commented Jul 10, 2016 at 20:03
  • 4
    @CharlieParker, to get both the index and the item, use enumerate() - next((idx, obj) for idx, obj in enumerate(objs) if obj.val==5) Commented Dec 13, 2016 at 9:01

2 Answers 2

755

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

10
  • 193
    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. Commented Jul 24, 2012 at 23:13
  • 31
    Python next() Commented Oct 19, 2013 at 4:29
  • 14
    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. Commented Mar 18, 2016 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
    Commented Mar 25, 2016 at 23:10
  • 2
    what if you want to find the item and the index too? Commented Jul 10, 2016 at 20:04
2

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))
4
  • 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 Commented Mar 26, 2012 at 9: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. Commented Mar 26, 2012 at 9: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 Commented Mar 26, 2012 at 11:35
  • 2
    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
    Commented Feb 7, 2023 at 9:45

Not the answer you're looking for? Browse other questions tagged or ask your own question.