Concatenation of a variant number of keys of a dictionary Python (recursion?)
Question: Hello Stackoverlow members,

I'm trying to concatenate keys (string) on a hand, and values (list) on the
other hand, of a dictionnary.

For your better understanding, here is what I have at the beginning:

    
    
    dict = {'bk1':
                {'k11': ['a1', 'b1', 'c1'],
                 'k12': ['a2', 'b2', 'c2']},
            'bk2':
                {'k21': ['d1', 'e1'],
                 'k22': ['d2', 'e2'],
                 'k23': ['d3', 'e3']},
            'bk3':
                {'k31': ['f1', 'g1', 'h1'],
                 'k32': ['f2', 'g2', 'h2']}
            }
    

And here is what I would like at the end:

    
    
    newdict = {'k11_k21_k31': ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1'],
               'k11_k21_k32': ['a1', 'b1', 'c1', 'd1', 'e1', 'f2', 'g2', 'h2'],
               'k11_k22_k31': ['a1', 'b1', 'c1', 'd2', 'e2', 'f1', 'g1', 'h1'],
               'k11_k22_k32': ['a1', 'b1', 'c1', 'd2', 'e2', 'f2', 'g2', 'h2'],
               'k11_k23_k31': ['a1', 'b1', 'c1', 'd3', 'e3', 'f1', 'g1', 'h1'],
               'k11_k23_k32': ['a1', 'b1', 'c1', 'd3', 'e3', 'f2', 'g2', 'h2'],
               'k12_k21_k31': ['a2', 'b2', 'c2', 'd1', 'e1', 'f1', 'g1', 'h1'],
               'k12_k21_k32': ['a2', 'b2', 'c2', 'd1', 'e1', 'f2', 'g2', 'h2'],
               'k12_k22_k31': ['a2', 'b2', 'c2', 'd2', 'e2', 'f1', 'g1', 'h1'],
               'k12_k22_k32': ['a2', 'b2', 'c2', 'd2', 'e2', 'f2', 'g2', 'h2'],
               'k12_k23_k31': ['a2', 'b2', 'c2', 'd3', 'e3', 'f1', 'g1', 'h1'],
               'k12_k23_k32': ['a2', 'b2', 'c2', 'd3', 'e3', 'f2', 'g2', 'h2']}
    

I wish to do that with:

  * a **variant** number of "big key" (bki), and for each bki, a **variant** number of key (kij). 

  * "Full combination" between "big keys". For example, I don't expect results like: 
    
        {'k11_k23': ['a1', 'b1', 'c1', 'd3', 'e3']}
    

where the "bk3" is missed.

I tried with imbricated "for" loops but the number of loops is depending on
the number of "big keys"...

Then, I felt that the problem could be solved with recursion (maybe?), but in
spite of my research and my will to implement it, I failed.

Any help with "recursive or not" solution would be strongly appreciated.

Thank you,

Mat

* * *

Whoaa, what a reactivity! Thanks a lot for all your quick answers, it works
**perfect!**


Answer: As suggested by @jksnw in the comments, you can use `itertools.product` to do
this:

    
    
    import itertools
    
    dct = {
        'bk1': {
            'k11': ['a1', 'b1', 'c1'],
            'k12': ['a2', 'b2', 'c2']
        },
        'bk2':{
            'k21': ['d1', 'e1'],
            'k22': ['d2', 'e2'],
            'k23': ['d3', 'e3']
        },
        'bk3': {
            'k31': ['f1', 'g1', 'h1'],
            'k32': ['f2', 'g2', 'h2']
        }
    }
    
    big_keys = dct.keys()
    small_keys = (dct[big_key].keys() for big_key in big_keys)
    
    res = {}
    for keys_from_each in itertools.product(*small_keys):
        key = "_".join(keys_from_each)
    
        value = []
        for big_key, small_key in zip(big_keys, keys_from_each):
            value.extend(dct[big_key][small_key])
    
        res[key] = value
    

So that:

    
    
    >>> res
    
    {'k11_k21_k31': ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1'],
     'k11_k21_k32': ['a1', 'b1', 'c1', 'd1', 'e1', 'f2', 'g2', 'h2'],
     'k11_k22_k31': ['a1', 'b1', 'c1', 'd2', 'e2', 'f1', 'g1', 'h1'],
     'k11_k22_k32': ['a1', 'b1', 'c1', 'd2', 'e2', 'f2', 'g2', 'h2'],
     'k11_k23_k31': ['a1', 'b1', 'c1', 'd3', 'e3', 'f1', 'g1', 'h1'],
     'k11_k23_k32': ['a1', 'b1', 'c1', 'd3', 'e3', 'f2', 'g2', 'h2'],
     'k12_k21_k31': ['a2', 'b2', 'c2', 'd1', 'e1', 'f1', 'g1', 'h1'],
     'k12_k21_k32': ['a2', 'b2', 'c2', 'd1', 'e1', 'f2', 'g2', 'h2'],
     'k12_k22_k31': ['a2', 'b2', 'c2', 'd2', 'e2', 'f1', 'g1', 'h1'],
     'k12_k22_k32': ['a2', 'b2', 'c2', 'd2', 'e2', 'f2', 'g2', 'h2'],
     'k12_k23_k31': ['a2', 'b2', 'c2', 'd3', 'e3', 'f1', 'g1', 'h1'],
     'k12_k23_k32': ['a2', 'b2', 'c2', 'd3', 'e3', 'f2', 'g2', 'h2']}
    

Here, `itertools.product` is used to get a list of the "small keys" that we
take from each block:

    
    
    >>> big_keys = dct.keys()
    >>> small_keys = (dct[big_key].keys() for big_key in big_keys)
    >>> list(itertools.product(*small_keys))
    
    [('k12', 'k22', 'k31'),
     ('k12', 'k22', 'k32'),
     ('k12', 'k23', 'k31'),
     ('k12', 'k23', 'k32'),
     ('k12', 'k21', 'k31'),
     ('k12', 'k21', 'k32'),
     ('k11', 'k22', 'k31'),
     ('k11', 'k22', 'k32'),
     ('k11', 'k23', 'k31'),
     ('k11', 'k23', 'k32'),
     ('k11', 'k21', 'k31'),
     ('k11', 'k21', 'k32')]
    

