0

I have come across this case a few times now: I have an array which contains an element I want and I want to select that element without knowing the index, and instead knowing some desired property (maybe it is a list of dictionaries and I want the dictionary elem such that elem['foo'] == 'bar').

A solution I have is do a filter (or more pythonically, a list comprehension) and then take the first element (often times I know that the filtered list will be a singleton, so taking the first is taking the only).

Ex. Given a list x = [{'foo': 'bar_1'}, {'foo': 'bar_2'}], I want the element whose 'foo' value is 'bar2'. So I do this :

y = [elem for elem in x if elem['foo'] == 'bar_2'][0]

Is there a more standard way of doing this? It seems like a very simple and common use case.

El Bert
  • 2,958
  • 1
  • 28
  • 36
tscizzle
  • 11,191
  • 15
  • 54
  • 88
  • possible duplicate of [Python: find first element in a sequence that matches a predicate](http://stackoverflow.com/questions/8534256/python-find-first-element-in-a-sequence-that-matches-a-predicate) – Ryan Haining Aug 28 '14 at 21:53

2 Answers2

1

If you run into this problem frequently then consider using a different data structure. Think about whether a list of dicts is really the best data structure for the problem you are solving. Or, if it is, add a dict to keep track of the information you require. In this case, as you build x, also build index:

index = {'bar2':x[1], 'bar1':x[0]}

Dict lookups are O(1), whereas any list-based lookup is O(n).

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
1

You can use a generator calling next on it to get the first match:

l = [{'foo': 'bar_1'}, {'foo': 'bar_2'}]
print next(d for d in l if d["foo"] == "bar_2")
{'foo': 'bar_2'}

d = (d for d in l if d["foo"] == "bar_2")
first = next(d)

You could also you itertools.dropwhile and if you want the first two elements:

In [52]: from itertools import dropwhile,takewhile
In [53]: l = [{'foo': 'bar_1'}, {'foo': 'bar_2'},{'foo': 'bar_2',"goo":"bar"}]
In [54]: d = dropwhile(lambda x: x["foo"] != "bar_2",l)   # drop elements whose where value of key "foo" is not equal to "bar_2"
In [55]: first = next(d)
In [56]: second = next(d)    
In [57]: first
Out[57]: {'foo': 'bar_2'}    
In [58]: second
Out[58]: {'foo': 'bar_2', 'goo': 'bar'}
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • Alternatively, `item, = (d for d in l if d["foo"] == "bar_2")` will assert that there is only one item in the list with the given property (which may, or may not be desired). – Dunes Aug 28 '14 at 20:28
  • @Dunes, yes true, I am not sure if the OP wants to check if there is only one match or just get the first. – Padraic Cunningham Aug 28 '14 at 20:34