11

I have a list of dicts e.g.

[{'name':'Bernard','age':7},{'name':'George','age':4},{'name':'Reginald','age':6}]

I'd like to check to see if a string value is the same as the 'name' value in any of the dicts in the list. For example 'Harold' would be False, but 'George' would be True.

I realise I could do this by looping through each item in the list, but I was wondering if there was a more efficient way?

cHao
  • 84,970
  • 20
  • 145
  • 172
chrism
  • 2,905
  • 8
  • 37
  • 40
  • 2
    even a build-in function would loop over the dict – Henrik P. Hessel May 18 '11 at 08:40
  • 1
    Why are you representing your data this way? Why don't you create a class `Person` with the attributes `name` and `age`, and then create a list or dict of those? – Björn Pollex May 18 '11 at 08:42
  • if not duplicated, very related: http://stackoverflow.com/questions/4391697/find-the-index-of-a-dict-within-a-list-by-matching-the-dicts-value – tokland May 18 '11 at 09:29
  • You could use `Person = collections.namedtuple('Person', 'name age')` instead of a dict as @Space_C0wb0y suggested: `L = [Person(**d) for d in L]` if individual items are readonly. – jfs May 18 '11 at 09:39

5 Answers5

17

No, there cannot be a more efficient way if you have just this list of dicts.

However, if you want to check frequently, you can extract a dictionary with name:age items:

l = [{'name':'Bernard','age':7},{'name':'George','age':4},{'name':'Reginald','age':6}]
d = dict((i['name'], i['age']) for i in l)

now you have d:

{'Bernard': 7, 'George': 4, 'Reginald': 6}

and now you can check:

'Harold' in d   -> False
'George' in d   -> True

It will be much faster than iterating over the original list.

eumiro
  • 207,213
  • 34
  • 299
  • 261
  • 7
    Or better yet, extract it into a set. `s = set((i['name'] for i in l))`. +1 – Chinmay Kanchi May 18 '11 at 08:58
  • For the minute I don't need it multiple times, but I will definitely keep this in mind for the future, thanks. – chrism May 18 '11 at 09:30
  • 2
    Note: there *is* a more efficient way if you have just a list of dicts e.g., you could sort it `L.sort(key=itemgetter('name'))` and use `bisect` module to perform a binary search. In practice you would use a set as @Chinmay Kanchi suggested or a linear search with `any()` as in @KennyTM's answer http://stackoverflow.com/questions/6041981/python-check-if-value-is-in-a-list-of-dicts/6042003#6042003 – jfs May 18 '11 at 09:46
15

The Proper Solution

There is a much more efficient way to do this than with looping. If you use operators.itemgetter you can do a simple if x in y check

#to simply check if the list of dicts contains the key=>value pair
'George' in map(itemgetter('name'), list_of_dicts)

#if you want to get the index 
index = map(itemgetter('name'), list_of_dicts).index("George") if 'George' in map(itemgetter('name'), list_of_dicts) else None
Community
  • 1
  • 1
AndrewSmiley
  • 1,933
  • 20
  • 32
  • 8
    Or if you're using Python 3: `list(map(itemgetter('name'), list_of_dicts)).index('George')`. Can also put it inside a `try.. except ValueError` in case there is no item found with `list.index()`, rather than doing two `itemgetter()` lookups. – timss Jun 15 '17 at 12:22
1

I think a list comprehension would do the trick here too.

names = [i['name'] for i in l]

Then use it like so:

'Bernard' in names (True)
'Farkle' in names (False)

Or a one liner (If it's only one check)

'Bernard' in [i['name'] for i in l] (True)
söze
  • 500
  • 1
  • 3
  • 13
0
l = [{'name':'Bernard','age':7},{'name':'George','age':4},{'name':'Reginald','age':6}]
search_for = 'George'
print True in map(lambda person: True if person['name'].lower() == search_for.lower() else False, l )
Vader
  • 3,675
  • 23
  • 40
0
smf = [{'name':'Bernard','age':7},{'name':'George','age':4},{'name':'Reginald','age':6}]
def names(d):
    for i in d:
        for key, value in i.iteritems():
             if key == 'name':
                 yield value


In [5]: 'Bernard' in names(smf)
Out[5]: True


In [6]: 'Bernardadf' in names(smf)
Out[6]: False
pod2metra
  • 256
  • 1
  • 6