简体   繁体   中英

Tuple of tuples to Dict of dicts using Dict Comprehension

I am executing the following sqlite command:

c.execute("SELECT surname,forename,count(*) from census_data group by surname, forename")

so that c.fetchall() is as follows:

(('Griffin','John', 7), 
 ('Griffin','James', 23), 
 ('Griffin','Mary',30), 
 ('Griffith', 'John', 4),
 ('Griffith','Catherine', 5)
)

Is it possible to construct a dict of the following form using a dict comprehension:

{'Griffin': {'John': 7, 'James': 23, 'Mary':30}, 'Griffith': {'John':4,'Catherine':5}}

this is as far as I got:

counts = {s:(f,c) for s,f,c in c.fetchall()}

which overwrites values. Im using python 3.

Coming with dict comprehension though with itertools.groupby magic:

from itertools import groupby

counts = {k: dict(_[1:] for _ in g) for k, g in groupby(c.fetchall(), key=lambda t: t[0])}
print(counts)

The output:

{'Griffin': {'John': 7, 'James': 23, 'Mary': 30}, 'Griffith': {'John': 4, 'Catherine': 5}}

You can use a collections.defaultdict to create the inner dicts automatically when needed:

from collections import defaultdict

data = (('Griffin','John', 7), 
 ('Griffin','James', 23), 
 ('Griffin','Mary',30), 
 ('Griffith', 'John', 4),
 ('Griffith','Catherine', 5)
)

out = defaultdict(dict)
for (name, first, value) in data:
    out[name][first] = value


# {'Griffin': {'John': 7, 'James': 23, 'Mary': 30}, 'Griffith': {'John': 4, 'Catherine': 5}}

Yes, with something like this.

my_query = (('Griffin','John', 7), ('Griffin','James', 23), ('Griffin','Mary',30), ('Griffith', 'John', 4), ('Griffith','Catherine', 5) )

dict_query = {}
for key1, key2, value in my_query:
    if key1 not in dict_query:
        dict_query[key1] = {}
    dict_query[key1][key2] = value

Edit1

More elegant.

from collections import defaultdict 
dict_query = defaultdict(dict) 
for key1, key2, value in my_query:
    dict_query[key1][key2] = value

Alternatively to defaultdict you can use the dictionary method setdefault() :

dct = {}

for k1, k2, v2 in c.fetchall():
    v1 = dct.setdefault(k1, {})
    v1[k2] = v2

or

for k1, k2, v2 in c.fetchall():
    dct.setdefault(k1, {})[k2] = v2

Result:

{'Griffin': {'John': 7, 'James': 23, 'Mary': 30},
 'Griffith': {'John': 4, 'Catherine': 5}}

You can solve it holding on itertools.groupby and functools.itemgetter .

from operator import itemgetter
from itertools import groupby

result = { name: dict(v for _, v in values) for name, values in groupby(((x[0], x[1:]) for x in c.fetchall()), itemgetter(0))}
print(result)
# {'Griffin': {'John': 7, 'James': 23, 'Mary': 30}, 'Griffith': {'John': 4, 'Catherine': 5}}

The outer loop splits the data between name and its values, that then groupby(...) collapses by name. The inner loop simply builds a dict with the corresponding values.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM