sid
sid

Reputation: 9

How to create a nested dictionary from multiple lists with duplicate values?

Suppose a set of three lists:

val = [1 2 2 3 5 5 5 6 8]
m = ['m2' 'm1' 'm2' 'm2' 'm1' 'm2' 'm2' 'm1' 'm1']
v = ['v9' 'v3' 'v7' 'v5' 'v1' 'v6' 'v8' 'v2' 'v4']

I'm trying to a create a nested dictionary where each element in val is used as a first order key. The elements in m shall describe the second order keys.

I already found out how to create the nested dictionary with

d = {}
for x, y, z in zip(val, m, v):
    d.setdefault(x,{})[y] = []

that creates the following structure

{1: {'m2': []}, 2: {'m2': [], 'm1': []}, 3: {'m2': []}, 5: {'m2': [], 'm1': []}, 6: {'m1': []}, 8: {'m1': []}}

And if i append z with

d.setdefault(x,{})[y].append(z)

i get

{1: {'m2': ['v9']}, 2: {'m2': ['v7'], 'm1': ['v3']}, 3: {'m2': ['v5']}, 5: {'m2': ['v8'], 'm1': ['v1']}, 6: {'m1': ['v2']}, 8: {'m1': ['v4']}} 

I also know how to create a dictionary that assigns the elements of v to the corresponding first order keys (i.e. the elements of val)

for elem in range(len(val)):
    if val[elem] not in d:
        d[val[elem]] = []
    d[val[elem]].append(v[elem])

resulting in

{1: ['v9'], 2: ['v3', 'v7'], 3: ['v5'], 5: ['v1', 'v6', 'v8'], 6: ['v2'], 8: ['v4']}

However i fail to see the last step to get the desired output:

{1: {'m2': ['v9']}, 2: {'m2': ['v7'], 'm1': ['v3']}, 3: {'m2': ['v5']}, 5: {'m2': ['v6', 'v8'], 'm1': ['v1']}, 6: {'m1': ['v2']}, 8: {'m1': ['v4']}}

(the element 'v6' is missing in the subdictionary with the key '5' and second order key 'm2' (i.e. 5: {'m2': ['v8']})

Upvotes: 0

Views: 105

Answers (2)

Austin
Austin

Reputation: 26039

Use itertools.groupby and zip with a nested dictionary-comprehension.

Example:

from itertools import groupby

val = [1, 2, 2, 3, 5, 5 ,5, 6, 8]
m = ['m2', 'm1', 'm2' ,'m2', 'm1', 'm2', 'm2', 'm1' ,'m1']
v = ['v9', 'v3' ,'v7', 'v5', 'v1', 'v6', 'v8', 'v2', 'v4']    

f1 = lambda x: x[:2]
f2 = lambda x: x[0]

print({k: {x[1]: [z[2] for z in y] for x, y in groupby(sorted(g, key=f1), f1)} for k, g in groupby(sorted(zip(val, m, v), key=f2), key=f2)})

# {1: {'m2': ['v9']}, 2: {'m1': ['v3'], 'm2': ['v7']}, 3: {'m2': ['v5']}, 5: {'m1': ['v1'], 'm2': ['v6', 'v8']}, 6: {'m1': ['v2']}, 8: {'m1': ['v4']}}

Upvotes: 1

Antoni Ramon
Antoni Ramon

Reputation: 11

Try to use defaultdict

from collections import defaultdict

val = [1, 2, 2, 3, 5, 5, 5, 6, 8]
m = ['m2', 'm1', 'm2', 'm2', 'm1', 'm2', 'm2', 'm1', 'm1']
v = ['v9', 'v3', 'v7', 'v5', 'v1', 'v6', 'v8', 'v2', 'v4']

dd = defaultdict(lambda: defaultdict(list))

for k1, k2, k3 in zip(val, m, v):
    dd[k1][k2].append(k3)

dd

Upvotes: 1

Related Questions