# Dictionary of dictionary - Get 2nd keys

I have a dictionary of dictionary such as d below, and I would like to get the list of all keys but not on the first level (i.e. ABC, DEF) but the one at the second level (i.e 0 and 1 for ABC ; 1 and 2 for DEF...)

I got this small code that does the job by reading all the dictionaries and by saving the keys (of second level):

d = {
'ABC' : {'0' : [(1,0.5), (2, 0.25)], '1' : [(1,0.4), (3,0.35)]},
'DEF' : {'1' : [(1,0.5), (4, 0.15)], '2' : [(2,0.3), (3,0.25)]},
'GHI' : {'0' : [(3,0.2), (4, 0.05)], '2' : [(2,0.2), (2,0.25)]},
}

keys2 = []
for small_d in d.itervalues():
keys2.extend(small_d.keys())

print set(keys2)
# set(['1', '0', '2'])


But I'm sure there is a more pythonic way to do it (and more efficient), any ideas?

Yes, there's a shorter (pythonic) way to do this using set comprehensions:

d = {
'ABC' : {'0' : [(1,0.5), (2, 0.25)], '1' : [(1,0.4), (3,0.35)]},
'DEF' : {'1' : [(1,0.5), (4, 0.15)], '2' : [(2,0.3), (3,0.25)]},
'GHI' : {'0' : [(3,0.2), (4, 0.05)], '2' : [(2,0.2), (2,0.25)]},
}
print {j for i in d.itervalues() for j in i}
# set(['1', '0', '2'])


Then if you just want a list object you can use list() to convert the set to list.

More, in your example you could just make keys2 a set to begin with and call keys2.add instead of extend.

import timeit
from itertools import chain

def m1(d):
return {j for i in d.itervalues() for j in i}

def m2(d):
keys2 = []
for small_d in d.itervalues():
keys2.extend(small_d.keys())

return set(keys2)

def m3(d):
return set(chain.from_iterable(d.itervalues()))

def m4(d):
keys2 = set([])
for small_d in d.itervalues():
keys2.update(small_d.keys())
return keys2

d = {
'ABC': {'0': [(1, 0.5), (2, 0.25)], '1': [(1, 0.4), (3, 0.35)]},
'DEF': {'1': [(1, 0.5), (4, 0.15)], '2': [(2, 0.3), (3, 0.25)]},
'GHI': {'0': [(3, 0.2), (4, 0.05)], '2': [(2, 0.2), (2, 0.25)]},
}

print(timeit.timeit(setup='from __main__ import m1, d', number=100000000))
print(timeit.timeit(setup='from __main__ import m2, d', number=100000000))
print(timeit.timeit(setup='from __main__ import m3, d', number=100000000))
print(timeit.timeit(setup='from __main__ import m4, d', number=100000000))

0.00136395591689003
0.0012866411345431158
0.0011429587956683193
0.0011374851827588035

• Since you’re using itervalues everywhere else, you might as well use it in m1. It might actually be a little bit faster too. Commented Jul 3, 2017 at 14:56
• Good call Matthias, I'll amend it when I'll have the time Commented Jul 3, 2017 at 16:11
• For a better comparison you might want to try larger dictionaries since the differences in the current benchmark are miniscule at best. Commented Jul 3, 2017 at 17:10

You can decrease code size using itertools:

from itertools import chain
return set(chain.from_iterable(d.itervalues()))


but i'm not sure if this version readable enough.

Also you can get rid of list and define keys2 as set:

keys2 = set([])
for small_d in d.itervalues():
keys2.update(small_d.keys())

{key for inner in d.values() for key in inner}


It is a set comprehension - to not include repeated values.
(You may use the list() function to convert it to the list.

The explanation: Read it from the end:
It is the set of each key in inner, where every inner is from the list d.values().