Search code examples
pythoniterationpython-itertools

Make all possible combinations of values from lists different size


I have a Python dict with lists of different length as key values.

{
    'basis1': ['key11', 'key12', 'key13', 'key14'],
    'basis2': ['key21'],
    'basis3': ['key31', 'key32', 'key33'],
    'basis4': ['key41', 'key42'],
    ...'basisX': ['keyX1', ..., 'keyXX']
}

How can I get all possible combinations of all dict key values (one value from each key to one value from another key)? I mean not only "basis1 + basis2 + ... basisX" values but also e.g. "basis2 + basis1 + basis3" and "basis3 + basisX" values.

I used to iterate with a "product" func from "itertools" for generating keywords by predetermined formulas. But there is a limitation because of the formulas and the number of lists that are in these formulas. But I need to make it so that does not depend on the number of lists that will be fed to the input of the function, as well as mix in a different order the values from the lists.

from itertools import product
...
...
...
# Each [keysX] is a list of values

    formula1 = [keys0] + [keys1]
    formula2 = [keys0] + [keys2]
    formula3 = [keys1] + [keys2]
    formula4 = [keys0] + [keys1] + [keys2]
    all_keywords = []

    for combo in product(*formula1):
        all_keywords.append(" ".join(combo))

    for combo in product(*formula2):
        all_keywords.append(" ".join(combo))

    for combo in product(*formula3):
        all_keywords.append(" ".join(combo))

    for combo in product(*formula4):
        all_keywords.append(" ".join(combo))

Solution

  • Note: Considering it is a permutations of all the elements of a powerset, the resulting iterable will be huge. So it is recommended, not to store it in memory, unless necessary. I have used generators for that reason.

    You can use the following:

    from itertools import chain, combinations, permutations
    
    def powerset(iterable):
        '''
        >>> list(powerset([1, 2, 3]))
        [(1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)]
        '''
        iterable = list(iterable)
        return chain.from_iterable(
            combinations(iterable, r)
            for r, _ in enumerate(iterable, start=1)
        )
    
    def perm_powerset(iterable):
        '''
        Given a powerset, returns a generator consisting
        all possible permutations of each element in the powerset.
        '''
        for each_set in powerset(iterable):
            for elem in permutations(each_set):
                yield elem
    
    d = {'k1': [1, 2], 'k2': [3], 'k4': [4]}
    
    for elem in perm_powerset(chain.from_iterable(d.values())):
        print(elem)
    

    Output:

    (1,)
    (2,)
    (3,)
    (4,)
    (1, 2)
    (2, 1)
    (1, 3)
    (3, 1)
    (1, 4)
    (4, 1)
    (2, 3)
    (3, 2)
    (2, 4)
    (4, 2)
    (3, 4)
    (4, 3)
    (1, 2, 3)
    (1, 3, 2)
    (2, 1, 3)
    (2, 3, 1)
    (3, 1, 2)
    (3, 2, 1)
    (1, 2, 4)
    (1, 4, 2)
    (2, 1, 4)
    (2, 4, 1)
    (4, 1, 2)
    (4, 2, 1)
    (1, 3, 4)
    (1, 4, 3)
    (3, 1, 4)
    (3, 4, 1)
    (4, 1, 3)
    (4, 3, 1)
    (2, 3, 4)
    (2, 4, 3)
    (3, 2, 4)
    (3, 4, 2)
    (4, 2, 3)
    (4, 3, 2)
    (1, 2, 3, 4)
    (1, 2, 4, 3)
    (1, 3, 2, 4)
    (1, 3, 4, 2)
    (1, 4, 2, 3)
    (1, 4, 3, 2)
    (2, 1, 3, 4)
    (2, 1, 4, 3)
    (2, 3, 1, 4)
    (2, 3, 4, 1)
    (2, 4, 1, 3)
    (2, 4, 3, 1)
    (3, 1, 2, 4)
    (3, 1, 4, 2)
    (3, 2, 1, 4)
    (3, 2, 4, 1)
    (3, 4, 1, 2)
    (3, 4, 2, 1)
    (4, 1, 2, 3)
    (4, 1, 3, 2)
    (4, 2, 1, 3)
    (4, 2, 3, 1)
    (4, 3, 1, 2)
    (4, 3, 2, 1)