[ACCEPTED]-Pythonic Way to reverse nested dictionaries-list-comprehension

Accepted answer
Score: 24

collections.defaultdict makes this pretty simple:

from collections import defaultdict
import pprint

data = {
 'Bob' : {'item1':3, 'item2':8, 'item3':6},
 'Jim' : {'item1':6, 'item4':7},
 'Amy' : {'item1':6,'item2':5,'item3':9,'item4':2}
}

flipped = defaultdict(dict)
for key, val in data.items():
    for subkey, subval in val.items():
        flipped[subkey][key] = subval

pprint.pprint(dict(flipped))

Output:

{'item1': {'Amy': 6, 'Bob': 3, 'Jim': 6},
 'item2': {'Amy': 5, 'Bob': 8},
 'item3': {'Amy': 9, 'Bob': 6},
 'item4': {'Amy': 2, 'Jim': 7}}

0

Score: 4

I totally agree that Ryan Ginstrom's answer 8 is the preferred way of doing this (for 7 all practical purposes).

But since the 6 question also explicitely asks:

Is it possible with a comprehension?

I thought 5 I'd chime in with a quick example as for 4 how to do this with a list comprehension 3 (it could be a good example for showing 2 how nested list comphrehensions can quickly 1 decrease readability).

import itertools

d = {
 'Bob' : {'item1':3, 'item2':8, 'item3':6},
 'Jim' : {'item1':6, 'item4':7},
 'Amy' : {'item1':6,'item2':5,'item3':9,'item4':2}
}

print dict([(x, dict([(k, d[k][x]) for k,v in d.items() if x in d[k]])) 
            for x in set(itertools.chain(*[z for z in d.values()]))])
Score: 1

This is easy enough to do (as others have 8 shown), but depending on your needs you 7 should also consider that for data with 6 several pieces of information where you 5 want to extract by any criterion, a database 4 might be the best tool. The built-in sqlite3 module 3 provides a low-overhead database that may, depending 2 on what you are doing, serve you better 1 than a nested dict.

Score: 0

Pandas can provide another option. Assume 1 data is the input dictionary.

import pandas as pd
output = {i:s.dropna().to_dict() for i, s in pd.DataFrame(data).T.iteritems()}
Score: 0

If you want just access reverse nested dictionaries, Save 2 memory if the dictionary is too large to 1 reverse.

class mdict2(dict):
    def __init__(self, parent, key1):
        self.parent = parent
        self.key1 = key1

    def __getitem__(self, key2):
        return self.parent.mirror[key2][self.key1]


class mdict(dict):
    def __init__(self, mirror):
        self.mirror = mirror

    def __getitem__(self, key):
        return mdict2(self, key)

d0 = {
 'Bob' : {'item1':3, 'item2':8, 'item3':6},
 'Jim' : {'item1':6, 'item4':7},
 'Amy' : {'item1':6,'item2':5,'item3':9,'item4':2}
}
d1 = mdict(d0)

d0['Amy']['item1'] == d1['item1']['Amy']
# True

More Related questions