Baz
Baz

Reputation: 13145

Tuple of Tuple to Dict of Dict with Dict Comprehension

Is it possible to convert a tuple of tuples like this:

l = (("a","aa",1),("a","bb",2),("a","cc",1),("b","ee",9),("b","gg",2))

to a dict of dicts like this:

{"a":{"aa":1,"bb":2,"cc":1}  "b": {"ee":9,"gg":2}}

using a dict comprehension like this:

r = {? for a,b,c in l}

Upvotes: 0

Views: 68

Answers (5)

DrD
DrD

Reputation: 561

here is what I would probably use, because I feel it is fairly readable...

l = (("a","aa",1),("a","bb",2),("a","cc",1),("b","ee",9),("b","gg",2))
from collections import defaultdict

d = defaultdict(dict)

for a,b,c in l:
    d[a][b]=c


print(d)

defaultdict(dict, {'a': {'aa': 1, 'bb': 2, 'cc': 1}, 'b': {'ee': 9, 'gg': 2}})

I did a crude benchmark against the other solution I like (groupby):

l = (("a","aa",1),("a","bb",2),("a","cc",1),("b","ee",9),("b","gg",2))
from collections import defaultdict
def dd():
    d = defaultdict(dict)
    for a,b,c in l:
        d[a][b]=c


def gb():
    {x: {z[1]: z[2] for z in y} for x, y in groupby(sorted(l, key=lambda x: x[0]), lambda x: x[0])}

def gb2():
    first = itemgetter(0)
    result = {key: {inner: value for _, inner, value in groups} for key, groups in groupby(l, key=first)}

%timeit dd()
%timeit gb()
%timeit gb2()

683 ns ± 1.33 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
2.11 µs ± 129 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
1.38 µs ± 29.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

(you see the added value of itemgetter vs having to do another import, also groupby expects a sorted input so for larger lists the performance penalty should be even worse...)

Upvotes: 0

Dani Mesejo
Dani Mesejo

Reputation: 61920

You could use groupby with a dictionary comprehension:

from itertools import groupby
from operator import itemgetter

l = (("a", "aa", 1), ("a", "bb", 2), ("a", "cc", 1), ("b", "ee", 9), ("b", "gg", 2))

first = itemgetter(0)
result = {key: {inner: value for _, inner, value in groups} for key, groups in groupby(l, key=first)}

print(result)

Output

{'b': {'gg': 2, 'ee': 9}, 'a': {'cc': 1, 'bb': 2, 'aa': 1}}

As mentioned by @juanpa.arrivillaga if the input is not sorted by the first element of each tuple, you need to sort it, for that you can do: l = sorted(l, key=first) before using the dictionary comprehension.

Upvotes: 5

JawguyChooser
JawguyChooser

Reputation: 1936

In my opinion, Daniel Mesejo's answer nailed it. That answer is based on groupby, but functionally speaking, groupby is a form of reduce. So, for variety, I'll offer here a solution with functools.reduce (which also uses defaultdict):

>>> from functools import reduce
>>> from collections import defaultdict
>>> l = (("a","aa",1),("a","bb",2),("a","cc",1),("b","ee",9),("b","gg",2))
>>> def update_and_return(acc, up):
...     acc[up[0]][up[1]] = up[2]
...     return acc
... 
>>> reduce(update_and_return, l, defaultdict(dict))
defaultdict(<type 'dict'>, {'a': {'aa': 1, 'cc': 1, 'bb': 2}, 'b': {'ee': 9, 'gg': 2}})

Even though this isn't a dictionary comprehension solution, I hope it will provide more context for someone wanting to understand the computation required to answer the question.

Upvotes: 0

Austin
Austin

Reputation: 26037

Use itertools.groupby:

from itertools import groupby

l = (("a","aa",1),("a","bb",2),("a","cc",1),("b","ee",9),("b","gg",2))

print({x: {z[1]: z[2] for z in y} for x, y in groupby(sorted(l, key=lambda x: x[0]), lambda x: x[0])})
# {'a': {'aa': 1, 'bb': 2, 'cc': 1}, 'b': {'ee': 9, 'gg': 2}}

Upvotes: 3

gold_cy
gold_cy

Reputation: 14226

Do you enjoy abusing built-ins for not their intended purpose? I would honestly use the other answers here but this is also another way to do this.

from collections import defaultdict

d = defaultdict(dict)

any(d[x[0]].update({x[1]: x[-1]}) for x in l)

print(d)

defaultdict(dict, {'a': {'aa': 1, 'bb': 2, 'cc': 1}, 'b': {'ee': 9, 'gg': 2}})

Using any will return a boolean which clearly is not the intended purpose here hence why I recommend other answers listed.

Upvotes: 0

Related Questions