Question

ok so i've got two dictionaries.

dictionary_1 = {'status': ['online', 'Away', 'Offline'],
                'Absent':['yes', 'no', 'half day']}
dictionary_2 = {'healthy': ['yes', 'no'],
                'insane': ['yes', 'no']

Now i need to combine them so that i get a new dictionary with:

{'status': ['online', 'online', 'away', 'away', 'Offline', 'Offline'],
 'Absent': ['yes', 'yes', 'no', 'no', 'half day', 'half day'],
 'healthy': ['yes', 'no', 'yes', 'no', 'yes', 'no'],
 'insane': ['yes', 'no', 'yes', 'no', 'yes', 'no']
}

This is an update which is very late but I found a way to do it without itertools if anyone is interested.

def cartesian_product(dict1, dict2):
    cartesian_dict = {}
    dict1_length = len(list(dict1.values())[0])
    dict2_length = len(list(dict2.values())[0])
    h = []
    for key in dict1:
        for value in dict1[key]:
            if not key in cartesian_dict:
                cartesian_dict[key] = []
                cartesian_dict[key].extend([value]*dict2_length)
            else:   
                cartesian_dict[key].extend([value]*dict2_length)
    for key in dict2:
        cartesian_dict[key] = dict2[key]*dict1_length
    return cartesian_dict
Was it helpful?

Solution

Best guess, based on @abarnert's interpretation (and assuming that the healthy and insane values in the current output are wrong, as they only have four members):

d1 = {'status': ['online', 'Away', 'Offline'] ,'absent':['yes', 'no', 'half day']}
d2 = {'healthy': ['yes', 'no'], 'insane': ['yes', 'no']}
d1_columns = zip(*d1.values())
d2_columns = zip(*d2.values())
col_groups = [c1+c2 for c1, c2 in itertools.product(d1_columns, d2_columns)]
rows = zip(*col_groups)
combined_keys = list(d1) + list(d2)
d_combined = dict(zip(combined_keys, rows))

which produces

>>> pprint.pprint(d_combined)
{'absent': ('yes', 'yes', 'no', 'no', 'half day', 'half day'),
 'healthy': ('yes', 'no', 'yes', 'no', 'yes', 'no'),
 'insane': ('yes', 'no', 'yes', 'no', 'yes', 'no'),
 'status': ('online', 'online', 'Away', 'Away', 'Offline', 'Offline')}

or, in your order,

>>> order = ["status", "absent", "healthy", "insane"]
>>> for k in order:
    print k, d_combined[k]
...     
status ('online', 'online', 'Away', 'Away', 'Offline', 'Offline')
absent ('yes', 'yes', 'no', 'no', 'half day', 'half day')
healthy ('yes', 'no', 'yes', 'no', 'yes', 'no')
insane ('yes', 'no', 'yes', 'no', 'yes', 'no')

OTHER TIPS

try this: it combines the two dict values, does the product, then re-separates them to turn into a dict.

import itertools

dictionary_1 = {'status': ['online', 'Away', 'Offline'],
                'Absent':['yes', 'no', 'half day']}
dictionary_2 = {'healthy': ['yes', 'no', 'recovering'],
                'insane': ['yes', 'no', 'partially' ]}

keys = dictionary_1.keys() + dictionary_2.keys()

first_values = zip(*dictionary_1.values())
# [('online','yes'), ('Away','no'),('Offline','half day')]

second_values = zip(*dictionary_2.values())

# this product will replicate the first_values 
# as many times as second_values exists
values_list = [i1+i2 for(i1,i2) in itertools.product(first_values,second_values)]

#re-separate the value lists for dict.
values = zip(*values_list)

new_dict = {key:list(values[i]) for i,key in enumerate(keys)}  

I encountered this problem some time ago making test cases. I have a package on pip now called 'looper' which extends itertools with some dictionary magic and other things I've found useful.

https://pypi.python.org/pypi/looper

What you want doesn't seem to be a full cartesian product of the two dictionarys, which would be 36 items long, combining each key d1[k1] * d1[k2] * d2[k1] * d2[k2].

Instead you seem to want d1[k1,k2] * d2[k1,k2], iterating through n evenly for each key. This is known as a zip function, and dict_zip does this for dictionaries.

from pprint import pprint
from looper import iterutil

dict_1 = {'status':  ['online', 'Away', 'Offline'],
          'Absent':  ['yes', 'no', 'half day']}
dict_2 = {'healthy': ['yes', 'no'],
          'insane':  ['yes', 'no']}

# the first thing to do is to zip the dictionaries up. This produces a dictionary for each value of n in d[k][n]
zipped_dict_1 = iterutil.dict_zip(**dict_1)
# {'Absent': 'yes', 'status': 'online'}
# {'Absent': 'no', 'status': 'Away'}
# {'Absent': 'half day', 'status': 'Offline'}
zipped_dict_2 = iterutil.dict_zip(**dict_2)
# {'healthy': 'yes', 'insane': 'yes'}
# {'healthy': 'no', 'insane': 'no'}


# Now the output is a list of flattened dictionaries, take the Cartesian product of them.
product_dict = iterutil.product(zipped_dict_1,zipped_dict_2) 
# ({'Absent': 'yes', 'status': 'online'}, {'healthy': 'yes', 'insane': 'yes'})
# ({'Absent': 'yes', 'status': 'online'}, {'healthy': 'no', 'insane': 'no'})
# ({'Absent': 'no', 'status': 'Away'}, {'healthy': 'yes', 'insane': 'yes'})
# ({'Absent': 'no', 'status': 'Away'}, {'healthy': 'no', 'insane': 'no'})
# ({'Absent': 'half day', 'status': 'Offline'}, {'healthy': 'yes', 'insane': 'yes'})
# ({'Absent': 'half day', 'status': 'Offline'}, {'healthy': 'no', 'insane': 'no'})

# The product function produces tuples which must be combined in to a final dictionary.
# Merge the dictionaries using imap
merged_dict =  iterutil.imap(lambda x: dict(x[0].items()+x[1].items()),product_dict)

for d in merged_dict:
    pprint(d)

OUTPUT

{'Absent': 'yes', 'healthy': 'yes', 'insane': 'yes', 'status': 'online'}
{'Absent': 'yes', 'healthy': 'no', 'insane': 'no', 'status': 'online'}
{'Absent': 'no', 'healthy': 'yes', 'insane': 'yes', 'status': 'Away'}
{'Absent': 'no', 'healthy': 'no', 'insane': 'no', 'status': 'Away'}
{'Absent': 'half day', 'healthy': 'yes', 'insane': 'yes', 'status': 'Offline'}
{'Absent': 'half day', 'healthy': 'no', 'insane': 'no', 'status': 'Offline'}

what I need is that the first dictionary to repeat the first value the amount of times of values in the second dictionary

OK, so you want to product each value in the first dictionary with the zip of the values in the second dictionary, and vice-versa.

To do that, you will have to unzip the two dictionaries' values, product the result, unzip that, zip each half of the resulting 2-tuple with the keys from the corresponding original dictionary, flatten the two resulting iterables of key-value pairs into one, and make a dictionary out of it. (You could instead flatten the values and zip that to the flattened keys from the two dicts, but I'm not sure that guarantees the right order…)

That sounds like a big mess, but that's what you're asking for.

Put your dictionaries into an array, then do something like this:

dictionaries[dict_1,dict_2]

product = {}
arr = []
for d in dictionaries:
    for k in dictionaries[d]:
        arr.append(d.get(k))
        product[k] = None
for k in product:
    product[k] = arr
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top