Home > Blockchain >  Two-keys dictionary into one key dictionary of lists
Two-keys dictionary into one key dictionary of lists

Time:11-24

I am trying to implement a simple task. I have a dictionary with keys (ti, wi)

y={('t1', 'w1'): 1, ('t2', 'w1'): 2, ('t3', 'w1'): 3, ('t1', 'w2'): 4, ('t2', 'w2'): 5, ('t3', 'w2'): 6}

I want to create a new dictionary where keys will be wi, and value is a list of all ti. So I want to have an output dictionary like:

{'w1': [1, 2, 3], 'w2': [4, 5, 6]}

I wrote the following code:

y={('t1', 'w1'): 1, ('t2', 'w1'): 2, ('t3', 'w1'): 3, ('t1', 'w2'): 4, ('t2', 'w2'): 5, ('t3', 'w2'): 6} 
y_w={}  
y_t=[]    
for w in range(1,3):
    y_t.clear()
    for t in range(1,4):
        print('t= ', t, 'w= ', w, 'y=' , y['t{0}'.format(t), 'w{0}'.format(w)])
        y_t.append(y['t{0}'.format(t), 'w{0}'.format(w)])            
    print(y_t)
    y_w['w{0}'.format(w)]=y_t
print(y_w)

But the result I am getting is

{'w1': [4, 5, 6], 'w2': [4, 5, 6]}

I can not understand where the first list disappeared? Can someone help me explain where I am wrong? Is there a nicer way to do it, maybe without for lops?

CodePudding user response:

Your problem lies in the assumption that setting the value in the dictionary somehow freezes the list.

It's no accident the lists have the same values: They are identical, two pointers to the same list. Observe:

>>> a_dict = {}
>>> a_list = []
>>> a_list.append(23)
>>> a_dict["a"] = a_list
>>> a_list.clear()
>>> a_list.append(42)
>>> a_dict["b"] = a_list
>>> a_dict
{'a': [42], 'b': [42]}

You could fix your solution by replacing y_t.clear() with y_t = [], which does create a new list:

y = {('t1', 'w1'): 1, ('t2', 'w1'): 2, ('t3', 'w1'): 3, ('t1', 'w2'): 4, ('t2', 'w2'): 5, ('t3', 'w2'): 6} 
y_w = {}      
for w in range(1,3):
    y_t = []
    for t in range(1,4):
        print('t= ', t, 'w= ', w, 'y=' , y['t{0}'.format(t), 'w{0}'.format(w)])
        y_t.append(y['t{0}'.format(t), 'w{0}'.format(w)])            
    print(y_t)
    y_w['w{0}'.format(w)]=y_t
print(y_w)

But there are, as you suspect, easier ways of doing this, for example the defaultdict solution shown by Riccardo Bucco.

CodePudding user response:

Try this:

from collections import defaultdict

d = defaultdict(list)
for k, v in y.items():
    d[k[1]].append(v)
d = dict(d)

CodePudding user response:

The line number 10 is causing the problem, if you replace it with y_t = [] it will work as you expect

CodePudding user response:

You could first find all unique keys:

unique_keys = set(list(zip(*k))[1])

and then create the dict with list-values using those:

{u: [v for k, v in y.items() if k[1] == u] for u in unique_keys}

CodePudding user response:

According to your output here's what you can try:

y = {('t1', 'w1'): 1, ('t2', 'w1'): 2, ('t3', 'w1'): 3, ('t1', 'w2'): 4, ('t2', 'w2'): 5, ('t3', 'w2'): 6}

def new_dict_with_keys(dictionary):
    new_dictionary = dict()

    # Go through the dictionary keys to read each key's value
    for tuple_key in dictionary: 
        if "w1" in tuple_key or "w2" in tuple_key:
            
            # Determine which key to use
            if "w1" in tuple_key:
                key = "w1"  
            else:
                key = "w2"

            # Check if the new dictionary has the "w1" or "w2" as a an item
            # If it does not, create a new list
            if new_dictionary.get(key) is None:
                new_dictionary[key] = list()
            
            # Append the value in the respective key
            new_dictionary[key].append(dictionary[tuple_key])
    
    # Return the dictionary with the items
    return new_dictionary

print(new_dict_with_keys(y))
# Prints: {'w1': [1, 2, 3], 'w2': [4, 5, 6]} 

CodePudding user response:

Here's a solution using itertools.groupby:

import itertools as it
from operator import itemgetter

items = sorted((k, v) for (_, k), v in y.items())
groups = it.groupby(items, key=itemgetter(0))
result = {k: [v for _, v in vs] for k, vs in groups}

# {'w1': [1, 2, 3], 'w2': [4, 5, 6]}
  • Related