0

I have a line of code like so:

event = [x for x in history if x.serial == serialized_event]

In all circumstances in my application, the resulting list will only ever contain a single item. But it seems to me it is going to iterate over the entire "history" list anyway looking for all possible matches.

This is unnecessary, especially since in most cases it will find what I'm looking for within the first few indices and it will effectively be the only result.

How can I make this more efficient?

Ivan
  • 1,427
  • 1
  • 16
  • 26
  • 1
    is `x.serial` sorted? – hashcode55 Jul 18 '16 at 17:32
  • 6
    sorry, didn't read the question properly... but I think that question is very close to what you want - http://stackoverflow.com/questions/2361426/what-is-the-best-way-to-get-the-first-item-from-an-iterable-matching-a-condition – Vince W. Jul 18 '16 at 17:33
  • Not inherently, but it could easily be made so. History is a Django queryset. – Ivan Jul 18 '16 at 17:34
  • 1
    What kind of data structure is `history`? Can you optimize that? For example dictionaries and sets have O(1) time complexity for membership checks. However if you always find the result in the first couple of indices it doesn't sound like this is a bottle neck so optimizing may end up costing more than it saves. – kylieCatt Jul 18 '16 at 17:34
  • The duplicate question nails it. Thanks, that one didn't come up in my searches. Voting to close. – Ivan Jul 18 '16 at 17:35
  • If `history` is a QuerySet and you only need one object, why not do the *filtering* from your DB – Moses Koledoye Jul 18 '16 at 17:41
  • @MosesKoledoye: Query minimization. I already have to request the full history for one task, but I need just a single event from it for another. I don't want to issue two queries if I can avoid it. – Ivan Jul 18 '16 at 17:44
  • 1
    QuerySets are lazy. More so, a search on the DB is more likely to be faster than in Python – Moses Koledoye Jul 18 '16 at 17:45
  • Hmm very well. If it's a lazy operation I'll try it that way, thanks! – Ivan Jul 18 '16 at 17:50

2 Answers2

2

If the one-liner is not necessary, just use a regular for loop with a break:

event = []
for x in history:
    if x.serial == serialized_event:
        event.append(x)
        break
Simon
  • 774
  • 4
  • 21
0

And if you would like to keep a one liner, use itertools.dropwhile to filter the undesired results, and fetch only the first matching item:

from itertools import dropwhile

event = next(dropwhile(lambda x: x.serial==serialized_event, history))
jsbueno
  • 99,910
  • 10
  • 151
  • 209