Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compute the product of 3 dictionaries and concatenate keys and values

Tags:

python

Assuming that I have 3 different dictionaries:

dict1 = {
  "A": "a"
}

dict2 = {
  "B": "b", 
  "C": "c",
  "D": "d", 
  "E": "e"
}

dict3 = {
  "F": "f", 
  "G": "g"
}

I want to compute the product of these dictionaries (excluding the product between dict2 and dict3) and combine both the keys and values where the keys are concatenated with _ and values with ' and '

The desired output would be a single dictionary:

{
  # dict1 x dict2 
  "A_B": "a and b", 
  "A_C": "a and c",
  "A_D": "a and d",
  "A_E": "a and e",

  # dict1 x dict3  
  "A_F": "a and f",
  "A_G": "a and g",

  # dict1 x dict2 x dict3 
  "A_B_F": "a and b and f",
  "A_B_G": "a and b and g",
  "A_C_F": "a and c and f",
  "A_C_G": "a and c and g",
  "A_D_F": "a and d and f",
  "A_D_G": "a and d and g",
  "A_E_F": "a and e and f",
  "A_E_G": "a and e and g"
} 

I had a look at the documentation for itertools but I was not able to understand how I can achieve the desired output.

like image 645
Tokyo Avatar asked Apr 11 '19 16:04

Tokyo


2 Answers

The function that will do the job is itertools.product. First, here is how you can print out the product dict1 x dict2 x dict3:

for t in product(dict1.items(), dict2.items(), dict3.items()): 
     k, v = zip(*t) 
     print("_".join(k), "-", " and ".join(v))   

Output:

A_B_F - a and b and f
A_B_G - a and b and g
A_C_F - a and c and f
A_C_G - a and c and g
A_D_F - a and d and f
A_D_G - a and d and g
A_E_F - a and e and f
A_E_G - a and e and g

Now, just populate a result dictionary:

result = {}
for t in product(dict1.items(), dict2.items(), dict3.items()): 
     k, v = zip(*t) 
     result["_".join(k)] = " and ".join(v)

You can now add to this dictionary the dict1 x dict2 and dict1 x dict3 products, that are even simpler to compute.


Based on @ShadowRanger's comment, here is a complete snippet:

import itertools
import pprint


dict1 = {
  "A": "a"
}

dict2 = {
  "B": "b",
  "C": "c",
  "D": "d",
  "E": "e"
}

dict3 = {
  "F": "f",
  "G": "g"
}


result = {}
for dicts in ((dict1, dict2), (dict1, dict3), (dict1, dict2, dict3)):
    for t in itertools.product(*(d.items() for d in dicts)):
        k, v = zip(*t)
        result["_".join(k)] = " and ".join(v)

pprint.pprint(result)

Output:

{'A_B': 'a and b',
 'A_B_F': 'a and b and f',
 'A_B_G': 'a and b and g',
 'A_C': 'a and c',
 'A_C_F': 'a and c and f',
 'A_C_G': 'a and c and g',
 'A_D': 'a and d',
 'A_D_F': 'a and d and f',
 'A_D_G': 'a and d and g',
 'A_E': 'a and e',
 'A_E_F': 'a and e and f',
 'A_E_G': 'a and e and g',
 'A_F': 'a and f',
 'A_G': 'a and g'}
like image 112
Right leg Avatar answered Oct 22 '22 03:10

Right leg


To produce all pairings, you can use two recursive generator functions: one to find the overall combinations of dictionaries, and the other to pair the keys and values:

def pair_dicts(data, c):
   if not data:
     keys, values = zip(*c)
     yield ('_'.join(keys), ' and '.join(values))
   else:
     for i in data[0]:
        yield from pair_dicts(data[1:], c+[i])

def combos(d, c = []):
  if len(c) == len(d):
    yield c
  else:
    if len(c) > 1:
      yield c
    for i in d:
      if all(h != i for h in c):
         yield from combos(d, c+[i])

new_d = [[list(c.items()) for c in i] for i in combos([dict1, dict2, dict3])]
final_result = dict(i for b in new_d for i in pair_dicts(b, []))

Output:

{'A_B': 'a and b', 'A_C': 'a and c', 'A_D': 'a and d', 'A_E': 'a and e', 'A_B_F': 'a and b and f', 'A_B_G': 'a and b and g', 'A_C_F': 'a and c and f', 'A_C_G': 'a and c and g', 'A_D_F': 'a and d and f', 'A_D_G': 'a and d and g', 'A_E_F': 'a and e and f', 'A_E_G': 'a and e and g', 'A_F': 'a and f', 'A_G': 'a and g', 'A_F_B': 'a and f and b', 'A_F_C': 'a and f and c', 'A_F_D': 'a and f and d', 'A_F_E': 'a and f and e', 'A_G_B': 'a and g and b', 'A_G_C': 'a and g and c', 'A_G_D': 'a and g and d', 'A_G_E': 'a and g and e', 'B_A': 'b and a', 'C_A': 'c and a', 'D_A': 'd and a', 'E_A': 'e and a', 'B_A_F': 'b and a and f', 'B_A_G': 'b and a and g', 'C_A_F': 'c and a and f', 'C_A_G': 'c and a and g', 'D_A_F': 'd and a and f', 'D_A_G': 'd and a and g', 'E_A_F': 'e and a and f', 'E_A_G': 'e and a and g', 'B_F': 'b and f', 'B_G': 'b and g', 'C_F': 'c and f', 'C_G': 'c and g', 'D_F': 'd and f', 'D_G': 'd and g', 'E_F': 'e and f', 'E_G': 'e and g', 'B_F_A': 'b and f and a', 'B_G_A': 'b and g and a', 'C_F_A': 'c and f and a', 'C_G_A': 'c and g and a', 'D_F_A': 'd and f and a', 'D_G_A': 'd and g and a', 'E_F_A': 'e and f and a', 'E_G_A': 'e and g and a', 'F_A': 'f and a', 'G_A': 'g and a', 'F_A_B': 'f and a and b', 'F_A_C': 'f and a and c', 'F_A_D': 'f and a and d', 'F_A_E': 'f and a and e', 'G_A_B': 'g and a and b', 'G_A_C': 'g and a and c', 'G_A_D': 'g and a and d', 'G_A_E': 'g and a and e', 'F_B': 'f and b', 'F_C': 'f and c', 'F_D': 'f and d', 'F_E': 'f and e', 'G_B': 'g and b', 'G_C': 'g and c', 'G_D': 'g and d', 'G_E': 'g and e', 'F_B_A': 'f and b and a', 'F_C_A': 'f and c and a', 'F_D_A': 'f and d and a', 'F_E_A': 'f and e and a', 'G_B_A': 'g and b and a', 'G_C_A': 'g and c and a', 'G_D_A': 'g and d and a', 'G_E_A': 'g and e and a'}
like image 39
Ajax1234 Avatar answered Oct 22 '22 05:10

Ajax1234