Home > Back-end >  Looking for the easiest way to add values to the list in subdictionaries
Looking for the easiest way to add values to the list in subdictionaries

Time:04-12

Looking for the easiest way to add values in subdictionaries. An example:

dict1 = { 
    "l": {"a": 1, "b": 2, "c": 3}, 
    "s": {"a": 4, "b": 5, "c": 6}, 
    "d": {"a": 7, "b": 8, "c": 9} }

I want to create output:

{"a": [1, 4, 7], "b": [2, 5, 8], "c": [3, 6, 9]}

Stuff that doesn't work:

from collections import defaultdict

dict1 = { 
    "l": {"a": 1, "b": 2, "c": 3}, 
    "s": {"a": 4, "b": 5, "c": 6}, 
    "d": {"a": 7, "b": 8, "c": 9} } 

new_dict = defaultdict(list)

for k in dict1:
    d2 = dict1.get(k)
    for k, v in d2.items():
        new_dict[k].append[v] 

Output: TypeError: 'builtin_function_or_method' object is not subscriptable

I did also something like this:

dict1 = { 
    "l": {"a": 1, "b": 2, "c": 3}, 
    "s": {"a": 4, "b": 5, "c": 6}, 
    "d": {"a": 7, "b": 8, "c": 9} } 

final_dict = {}
# list_dict = []

for k, v in dict1.items():
    dict_new = dict1[k]
    list_dict = []
    for new_k, new_v in dict_new.items():
        list_dict.append(new_v)
        final_dict[new_k] = list_dict
        print("list_dict ", list_dict)
print("final_dict", final_dict) 

...but the output is: {'a': [7, 8, 9], 'b': [7, 8, 9], 'c': [7, 8, 9]}

When I put list_dict = [] outside for loop, then the output is: {'a': [1, 2, 3, 4, 5, 6, 7, 8, 9], 'b': [1, 2, 3, 4, 5, 6, 7, 8, 9], 'c': [1, 2, 3, 4, 5, 6, 7, 8, 9]}

I wonder do I need some sort of recursion method here to group by all sub-keys (a, b, c) and append all related values, or maybe there is a need for dictionary comprehension.

CodePudding user response:

The output suggests you want to loop over the values of dict1. One option is to use collections.defaultdict:

from collections import defaultdict
out = defaultdict(list)
for d in dict1.values():
    for k,v in d.items():
        out[k].append(v)
out = dict(out)

Another option is to use cytoolz.dicttoolz.merge_with:

from cytoolz.dicttoolz import merge_with
out = merge_with(list, *dict1.values())

Yet another option is to use operator.itemgetter in a comprehension:

from operator import itemgetter
vals = list(dict1.values())
keys = chain.from_iterable(map(dict.keys, vals))
out = {k: list(map(itemgetter(k), vals)) for k in keys}

Output:

{'a': [1, 4, 7], 'b': [2, 5, 8], 'c': [3, 6, 9]}

CodePudding user response:

new_dict[k].append[v] should be new_dict[k].append(v) - you're not calling the method (as you should), you're trying to index into it as a list/dict. There is also no reason to look up the value of k in the dict immediately after iterating it - you can use values() to get only the values instead.

for inner_dict in dict1.values():
    for k, v in inner_dict.items():
        new_dict[k].append(v) 

CodePudding user response:

Adding to the previous answers, if you wished to do this in an one-liner you could do it as such:

dict1 = { 
    "l": {"a": 1, "b": 2, "c": 3}, 
    "s": {"a": 4, "b": 5, "c": 6}, 
    "d": {"a": 7, "b": 8, "c": 9}
}

out = dict(zip(dict1.keys(), map(list, zip(*map(dict.values, dict1.values())))))
print(out)

Output:

{'l': [1, 4, 7], 's': [2, 5, 8], 'd': [3, 6, 9]}
  • Related