377

I'm basically looking for a python version of Combination of List<List<int>>

Given a list of lists, I need a new list that gives all the possible combinations of items between the lists.

[[1,2,3],[4,5,6],[7,8,9,10]] -> [[1,4,7],[1,4,8],...,[3,6,10]]

The number of lists is unknown, so I need something that works for all cases. Bonus points for elegance!

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Lin
  • 5,235
  • 8
  • 31
  • 30

9 Answers9

620

you need itertools.product:

>>> import itertools
>>> a = [[1,2,3],[4,5,6],[7,8,9,10]]
>>> list(itertools.product(*a))
[(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 4, 10), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 5, 10), (1, 6, 7), (1, 6, 8), (1, 6, 9), (1, 6, 10), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 4, 10), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 5, 10), (2, 6, 7), (2, 6, 8), (2, 6, 9), (2, 6, 10), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 4, 10), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 5, 10), (3, 6, 7), (3, 6, 8), (3, 6, 9), (3, 6, 10)]
trss
  • 915
  • 1
  • 19
  • 35
SilentGhost
  • 307,395
  • 66
  • 306
  • 293
  • 36
    Could someone explain the meaning of the asterisk in `*a`? – Serrano Feb 04 '13 at 20:20
  • 68
    `*a` means these are arguments being passed to the function or method. `def fn(a,b,c):` would respond to `fn(*[1,2,3])` [reference](http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/) – mjallday Feb 12 '13 at 23:37
  • 1
    @mjallday, would it be possible to add also these combinations: (7,4,1),(8,4,1),(9,4,1),(10,4,1),(7,5,1),(8,5,1),(9,5,1),(10,5,1) etc? – Reman Feb 17 '16 at 15:19
  • 2
    @Reman It's not entirely clear what you want to get but if it is, for example, also the reverse of each tuple you can use a a wrapper function that takes `a` as input, iterates over `itertools.product(*a)` and `yield`s both the tuple produced by `itertools` and a reverse version (e.g. create a list, `reverse()` it and convert back to tuple). Best ask a new question. – Joachim Wagner Feb 26 '20 at 11:06
  • if you are familiar with javascript `[*a]` would be the same the js spread operator `[...a]`. This is also true for dicts – Kurtis Streutker Dec 26 '20 at 20:29
28

The most elegant solution is to use itertools.product in python 2.6.

If you aren't using Python 2.6, the docs for itertools.product actually show an equivalent function to do the product the "manual" way:

def product(*args, **kwds):
    # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
    # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
    pools = map(tuple, args) * kwds.get('repeat', 1)
    result = [[]]
    for pool in pools:
        result = [x+[y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)
vvvvv
  • 25,404
  • 19
  • 49
  • 81
Jarret Hardie
  • 95,172
  • 10
  • 132
  • 126
27

Simply use itertools.product:

listOLists = [[1,2,3],[4,5,6],[7,8,9,10]]
for l in itertools.product(*listOLists):
    print(l)
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
16

Nothing wrong with straight up recursion for this task, no need for external dependencies, and if you need a version that works with strings, this might fit your needs:

combinations = []

def combine(terms, accum):
    last = (len(terms) == 1)
    n = len(terms[0])
    for i in range(n):
        item = accum + terms[0][i]
        if last:
            combinations.append(item)
        else:
            combine(terms[1:], item)


>>> a = [['ab','cd','ef'],['12','34','56']]
>>> combine(a, '')
>>> print(combinations)
['ab12', 'ab34', 'ab56', 'cd12', 'cd34', 'cd56', 'ef12', 'ef34', 'ef56']
duanev
  • 936
  • 13
  • 17
6

Numpy can do it:

 >>> import numpy
 >>> a = [[1,2,3],[4,5,6],[7,8,9,10]]
 >>> [list(x) for x in numpy.array(numpy.meshgrid(*a)).T.reshape(-1,len(a))]
[[ 1, 4, 7], [1, 5, 7], [1, 6, 7], ....]
Community
  • 1
  • 1
Diamantatos Paraskevas
  • 3,874
  • 1
  • 20
  • 15
5

One can use base python for this. The code needs a function to flatten lists of lists:

def flatten(B):    # function needed for code below;
    A = []
    for i in B:
        if type(i) == list: A.extend(i)
        else: A.append(i)
    return A

Then one can run:

L = [[1,2,3],[4,5,6],[7,8,9,10]]

outlist =[]; templist =[[]]
for sublist in L:
    outlist = templist; templist = [[]]
    for sitem in sublist:
        for oitem in outlist:
            newitem = [oitem]
            if newitem == [[]]: newitem = [sitem]
            else: newitem = [newitem[0], sitem]
            templist.append(flatten(newitem))

outlist = list(filter(lambda x: len(x)==len(L), templist))  # remove some partial lists that also creep in;
print(outlist)

Output:

[[1, 4, 7], [2, 4, 7], [3, 4, 7], 
[1, 5, 7], [2, 5, 7], [3, 5, 7], 
[1, 6, 7], [2, 6, 7], [3, 6, 7], 
[1, 4, 8], [2, 4, 8], [3, 4, 8], 
[1, 5, 8], [2, 5, 8], [3, 5, 8], 
[1, 6, 8], [2, 6, 8], [3, 6, 8], 
[1, 4, 9], [2, 4, 9], [3, 4, 9], 
[1, 5, 9], [2, 5, 9], [3, 5, 9], 
[1, 6, 9], [2, 6, 9], [3, 6, 9], 
[1, 4, 10], [2, 4, 10], [3, 4, 10], 
[1, 5, 10], [2, 5, 10], [3, 5, 10], 
[1, 6, 10], [2, 6, 10], [3, 6, 10]]
rnso
  • 23,686
  • 25
  • 112
  • 234
1

This mostly mimics solutions like Answer by Jarret Hardie using itertools.product, but has these distinctions:

  • this passes parameters to itertools.product in-line, instead of via variable a - so no *args syntax needed on the inline parameters
  • if your mypy type-linter acts like mine, and you can get your code to otherwise "work" with the *args syntax with inline product parameters (like product(*[[1,2,3],[4,5,6],[7,8,9,10]])), mypy might still fail it (with something like error: No overload variant of "product" matches argument type "List[object]")
  • So solution to that mypy, is to not use *args syntax, like this:
    >>> import itertools
    >>> list(itertools.product([1,2,3],[4,5,6],[7,8,9,10]))
    [(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 4, 10), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 5, 10), (1, 6, 7), (1, 6, 8), (1, 6, 9), (1, 6, 10), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 4, 10), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 5, 10), (2, 6, 7), (2, 6, 8), (2, 6, 9), (2, 6, 10), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 4, 10), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 5, 10), (3, 6, 7), (3, 6, 8), (3, 6, 9), (3, 6, 10)]
cellepo
  • 4,001
  • 2
  • 38
  • 57
1

This answer isn't as clean as using itertools but the ideas could be useful.

Drawing inspiration from the construction of zip() here, we could do the following.

>>> a = iter([[1,2,3],[4,5,6],[7,8,9,10]])
>>> sentinel = object()
>>> result = [[]]
>>> while True:
>>>     l = next(a,sentinel)
>>>     if l == sentinel:
>>>         break
>>>     result = [ r + [digit] for r in result for digit in l]
>>> print(result)
[[1, 4, 7], [1, 4, 8], [1, 4, 9], [1, 4, 10], [1, 5, 7], [1, 5, 8], [1, 5, 9], [1, 5, 10], [1, 6, 7], [1, 6, 8], [1, 6, 9], [1, 6, 10], [2, 4, 7], [2, 4, 8], [2, 4, 9], [2, 4, 10], [2, 5, 7], [2, 5, 8], [2, 5, 9], [2, 5, 10], [2, 6, 7], [2, 6, 8], [2, 6, 9], [2, 6, 10], [3, 4, 7], [3, 4, 8], [3, 4, 9], [3, 4, 10], [3, 5, 7], [3, 5, 8], [3, 5, 9], [3, 5, 10], [3, 6, 7], [3, 6, 8], [3, 6, 9], [3, 6, 10]]

We use a as an iterator in order to successively get the next item of it without needing to know how many there are a priori. The next command will output sentinel (which is an object created solely to make this comparison, see here for some explanation) when we run out of lists in a, causing the if statement to trigger so we break out of the loop.

Kweku A
  • 11
  • 2
  • 1
    Indeed this is more or less what `itertools.product` does, -- see [here](https://docs.python.org/3/library/itertools.html#itertools.product) -- except the use of a sentinel while `itertools.product` uses `yield`. – Kweku A Jul 02 '21 at 13:20
-1
from itertools import product 
list_vals = [['Brand Acronym:CBIQ', 'Brand Acronym :KMEFIC'],['Brand Country:DXB','Brand Country:BH']]
list(product(*list_vals))

Output:

[('Brand Acronym:CBIQ', 'Brand Country :DXB'),
('Brand Acronym:CBIQ', 'Brand Country:BH'),
('Brand Acronym :KMEFIC', 'Brand Country :DXB'),
('Brand Acronym :KMEFIC', 'Brand Country:BH')]

MartenCatcher
  • 2,713
  • 8
  • 26
  • 39
Kez
  • 9
  • 1