5

I have multiple dictionaries which contain data depending of their business value, e.g.:

companies = {'google': 'value_1', 'facebook': 'value_2'}
names = {'alex': 'value_3', 'john': 'value_4'}
...

I need to check whether a variable x is contained in any of these dictionaries, and to identify in which of these dictionaries it is contained. The number of such dictionaries can be huge, so checking them manually is not very effective. Is there something more pythonic than

if x in companies:
    pass    # do something
elif x in names:
    pass    # do something
...
Boseong Choi
  • 2,566
  • 9
  • 22
dj antivaxx
  • 301
  • 2
  • 16

7 Answers7

4

Simple/quick to code: loop in the list of dictionaries, stop when you find it.

But complexity isn't good if you perform several searches. Instead, create a dictionary of your dictionaries.

  • The keys are the union of keys
  • The values are lists of couples (value, origin dict)

like this:

companies = {'google': 'value_1', 'facebook': 'value_2'}
names = {'alex': 'value_3', 'john': 'value_4'}

import collections

c = collections.defaultdict(list)

for d in [companies,names]:
    for k,v in d.items():
        c[k].append((v,d))

now:

print(c.get('google'))

prints:

[('value_1', {'google': 'value_1', 'facebook': 'value_2'})

Now if I add a common key in both dicts:

names = {'alex': 'value_3', 'john': 'value_4', 'facebook':'value_5'}
print(c.get('facebook'))

we get a list of all values and the origin dictionaries:

[('value_2', {'google': 'value_1', 'facebook': 'value_2'}),
 ('value_5', {'alex': 'value_3', 'john': 'value_4', 'facebook': 'value_5'})]

With that solution, even if you have a lot of dictionaries, the lookup is always O(1) once the new big dictionary is built. The build is amortized after 2 or 3 lookups.

Above, we see that the origin dictionary has been retained. Now you can choose how you identify this dictionary. I chose to put the reference itself as I had no constraint.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
3

You could make a list with those dictionaries :

dictionaries = [companies, names]

for dictionary in dictionaries:
        if keyword in dictionary:
lmsec
  • 324
  • 2
  • 8
Noah
  • 193
  • 11
3

I'd use a list to hold all of the dictionaries, and then just filter out the ones that don't contain your key using a list comprehension:

companies = {'google': 'value_1', 'facebook': 'value_2'}
names = {'alex': 'value_3', 'john': 'value_4'}

x = 'john'

dicts = [companies, names]

contains_key = [d for d in dicts if x in d]

For a large number of dictionaries, say thousands, this doesn't scale as well as @Jean-François Fabre's answer, but it's a simple approach

C.Nivs
  • 12,353
  • 2
  • 19
  • 44
3

Instead of looping over all the dictionaries every time for every x you have, you could build a dictionary of all the keys, with the names of the dictionaries that have that key as value.

In this way you will loop once over all the dictionaries to build your 'look up table' and then every access to an 'x' won't need any looping anymore.

For example:

my_dicts = {'companies': {'google': 'value_1', 'facebook': 'value_2', 'alex': 'yo'},
            'names': {'alex': 'value_3', 'john': 'value_4'}}

# build the lookup dict
lookup = {}
for dk, dv in my_dicts.items():
    for k in dv.keys():
        dl = lookup.get(k, [])
        lookup[k] = dl + [dk]

Now you can directly access the dictionaries that have your x key:

x = 'alex'
dict_names = lookup[x]

for dn in dict_names:
    # do something on my_dict[dn]
    print(dn)

companies names

dzang
  • 2,160
  • 2
  • 12
  • 21
2

The way to:

  • Use filter to find the dicts where the search_key is in
  • Use next() to iterate once to get the first one (to get any in fact)
  • use the default param to avoid the StopIteration (you can return dict also next(..., {})
def find_dict(x, *dicts):
    return next(filter(lambda d: x in d, dicts), None)

Use :

if __name__ == '__main__':
    companies = {'google': 'value_1', 'facebook': 'value_2'}
    names = {'alex': 'value_3', 'john': 'value_4'}
    x = 'alex'

    print(find_dict(x, companies, names)) # {'alex': 'value_3', 'john': 'value_4'}
    print(find_dict('foo', companies, names)) # None
azro
  • 53,056
  • 7
  • 34
  • 70
2

An easy way would be to put the dictionaries in a list, iterate each dictionary, and check if x exists in each dictionary:

from json import dumps

companies = {'google': 'value_1', 'facebook': 'value_2'}
names = {'alex': 'value_3', 'john': 'value_4'}

lst = [companies, names]

x = 'google'

for dic in lst:
    if x in dic:
        print('%s exists in dict %s' %(key, dumps(dic)))

# google exists in dict {"google": "value_1", "facebook": "value_2"}

But this is slow because you need to iterate every dictionary in the list for a lookup. This will be O(D) for each lookup, where D is the number of dictionaries in the list.

A faster way would be to use a defaultdict(list) to collect the dictionaries for each key, then subsequent look ups are O(1). However building this dictionary will be a O(D * K) operation(D = number of dictionaries, K = number of keys per dictionary), because we need to iterate every dict and its keys. If your doing a lot of look ups then this conversion will be worthwhile in the long run.

from collections import defaultdict
from json import dumps

companies = {'google': 'value_1', 'facebook': 'value_2'}
names = {'alex': 'value_3', 'john': 'value_4'}

lst = [companies, names]

x = 'google'

all_dicts = defaultdict(list)
for dic in lst:
    for key in dic:
        all_dicts[key].append(dic)

print("%s exists in these dictionaries : %s" % (x, dumps(all_dicts[x])))
# google exists in these dictionaries : [{"google": "value_1", "facebook": "value_2"}]
RoadRunner
  • 25,803
  • 6
  • 42
  • 75
1

Python dictionary keyview objects are actually set-like. You can make a set out of them easily:

>>> a = {'a':1, 'b':2}
>>> b = {'a':2, 'c':3}
>>> a.keys() | b.keys()
{'a', 'b', 'c'}

Now all you need to do is check membership in the set

>>> if x in that_set: ...

If you have a large number of dictionaries, you can look at this answer for how to make a union of many sets, but keep in mind that set.union(dict.keys()) is not legal...

>>> set.union(a.keys())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor 'union' for 'set' objects doesn't apply to a 'dict_keys' object

so you'd need to do something like

>>> all_keys = set.union(*(set(d) for d in all_dicts))

explicitly casting each dictionary to a set in the comprehension.

Of course, you could also merge all the dictionaries, to a similar effect:

>>> all_dicts = {**dict1, **dict2, **dict3}
>>> "key" in all_dicts

This is a still very efficient way to find at least one value in all the dictionaries for the given key, even though it still doesn't tell you which of the dictionaries the key was in. For that, I'm afraid you must iterate. azro's fine answer tells you how to do that.

kojiro
  • 74,557
  • 19
  • 143
  • 201
  • "and to identify in which of these dictionaries it is contained": how does your answer answer to that? – Jean-François Fabre Apr 02 '20 at 12:35
  • Hard to find the correct dict after miwing the keys – azro Apr 02 '20 at 12:35
  • Thanks, I've updated my answer with two alternatives -- I don't believe it's possible to identify the original dictionary other than something like brute force, but it's also possible that OP or any future reader just wants to get the value of any matching key in any of the dictionaries. I've provided a solution to that, too. – kojiro Apr 02 '20 at 12:46
  • Also the last approach doesn't handle duplicate keys between the dictionaries. – RoadRunner Apr 02 '20 at 13:08