216
votes

I’m having trouble wrapping my head around a algorithm I’m try to implement. I have two lists and want to take particular combinations from the two lists.

Here’s an example.

names = ['a', 'b']
numbers = [1, 2]

the output in this case would be:

[('a', 1), ('b', 2)]
[('b', 1), ('a', 2)]

I might have more names than numbers, i.e. len(names) >= len(numbers). Here's an example with 3 names and 2 numbers:

names = ['a', 'b', 'c']
numbers = [1, 2]

output:

[('a', 1), ('b', 2)]
[('b', 1), ('a', 2)]
[('a', 1), ('c', 2)]
[('c', 1), ('a', 2)]
[('b', 1), ('c', 2)]
[('c', 1), ('b', 2)]
11
@dm03514 I saw that, and found examples for somewhat similar goals using itertools but I'm prototyping in python but will write the final code in another language so I do not want to use any tools that are not avail elseway. - user1735075
What you are asking for doesn't really make sense. If the first list contains A,B,C and the second contains 1,2, what result would you expect? It could be done if the example you gave had 4 different results of one letter and one number each (A1, A2, B1, B2), or if both lists had to have the same size. - interjay
I agree with interjay. Please specify the result in the non-equal size case, otherwise it's not possible to provide a general solution. - Bakuriu
Note for future folks dup-ing questions to this one: There is a decent chance that Get the cartesian product of a series of lists? is a better duplicate target (lots of stuff that should use product is being duplicated here, even though this question is not properly solved that way). In rarer cases, All possible replacements of two lists? may be better (when selecting a value from one of two lists at each index, which is a product solution again, with a zip prepass). - ShadowRanger

11 Answers

121
votes

Note: This answer is for the specific question asked above. If you are here from Google and just looking for a way to get a Cartesian product in Python, itertools.product or a simple list comprehension may be what you are looking for - see the other answers.


Suppose len(list1) >= len(list2). Then what you appear to want is to take all permutations of length len(list2) from list1 and match them with items from list2. In python:

import itertools
list1=['a','b','c']
list2=[1,2]

[list(zip(x,list2)) for x in itertools.permutations(list1,len(list2))]

Returns

[[('a', 1), ('b', 2)], [('a', 1), ('c', 2)], [('b', 1), ('a', 2)], [('b', 1), ('c', 2)], [('c', 1), ('a', 2)], [('c', 1), ('b', 2)]]
591
votes

The simplest way is to use itertools.product:

a = ["foo", "melon"]
b = [True, False]
c = list(itertools.product(a, b))
>> [("foo", True), ("foo", False), ("melon", True), ("melon", False)]
195
votes

May be simpler than the simplest one above:

>>> a = ["foo", "bar"]
>>> b = [1, 2, 3]
>>> [(x,y) for x in a for y in b]  # for a list
[('foo', 1), ('foo', 2), ('foo', 3), ('bar', 1), ('bar', 2), ('bar', 3)]
>>> ((x,y) for x in a for y in b)  # for a generator if you worry about memory or time complexity.
<generator object <genexpr> at 0x1048de850>

without any import

27
votes

I was looking for a list multiplied by itself with only unique combinations, which is provided as this function.

import itertools
itertools.combinations(list, n_times)

Here as an excerpt from the Python docs on itertools That might help you find what your looking for.

Combinatoric generators:

Iterator                                 | Results
-----------------------------------------+----------------------------------------
product(p, q, ... [repeat=1])            | cartesian product, equivalent to a 
                                         |   nested for-loop
-----------------------------------------+----------------------------------------
permutations(p[, r])                     | r-length tuples, all possible 
                                         |   orderings, no repeated elements
-----------------------------------------+----------------------------------------
combinations(p, r)                       | r-length tuples, in sorted order, no 
                                         |   repeated elements
-----------------------------------------+----------------------------------------
combinations_with_replacement(p, r)      | r-length tuples, in sorted order, 
                                         | with repeated elements
-----------------------------------------+----------------------------------------
product('ABCD', repeat=2)                | AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD
permutations('ABCD', 2)                  | AB AC AD BA BC BD CA CB CD DA DB DC
combinations('ABCD', 2)                  | AB AC AD BC BD CD
combinations_with_replacement('ABCD', 2) | AA AB AC AD BB BC BD CC CD DD
14
votes

the best way to find out all the combinations for large number of lists is:

import itertools
from pprint import pprint

inputdata = [
    ['a', 'b', 'c'],
    ['d'],
    ['e', 'f'],
]
result = list(itertools.product(*inputdata))
pprint(result)

the result will be:

[('a', 'd', 'e'),
 ('a', 'd', 'f'),
 ('b', 'd', 'e'),
 ('b', 'd', 'f'),
 ('c', 'd', 'e'),
 ('c', 'd', 'f')]
12
votes

You might want to try a one line list comprehension:

>>> [name+number for name in 'ab' for number in '12']
['a1', 'a2', 'b1', 'b2']
>>> [name+number for name in 'abc' for number in '12']
['a1', 'a2', 'b1', 'b2', 'c1', 'c2']
10
votes

Or the KISS answer for short lists:

[(i, j) for i in list1 for j in list2]

Not as performant as itertools but you're using python so performance is already not your top concern...

I like all the other answers too!

9
votes

a tiny improvement for the answer from interjay, to make the result as a flatten list.

>>> list3 = [zip(x,list2) for x in itertools.permutations(list1,len(list2))]
>>> import itertools
>>> chain = itertools.chain(*list3)
>>> list4 = list(chain)
[('a', 1), ('b', 2), ('a', 1), ('c', 2), ('b', 1), ('a', 2), ('b', 1), ('c', 2), ('c', 1), ('a', 2), ('c', 1), ('b', 2)]

reference from this link

6
votes

Without itertools as a flattened list:

[(list1[i], list2[j]) for i in range(len(list1)) for j in range(len(list2))]

or in Python 2:

[(list1[i], list2[j]) for i in xrange(len(list1)) for j in xrange(len(list2))]
5
votes

The better answers to this only work for specific lengths of lists that are provided.

Here's a version that works for any lengths of input. It also makes the algorithm clear in terms of the mathematical concepts of combination and permutation.

from itertools import combinations, permutations
list1 = ['1', '2']
list2 = ['A', 'B', 'C']

num_elements = min(len(list1), len(list2))
list1_combs = list(combinations(list1, num_elements))
list2_perms = list(permutations(list2, num_elements))
result = [
  tuple(zip(perm, comb))
  for comb in list1_combs
  for perm in list2_perms
]

for idx, ((l11, l12), (l21, l22)) in enumerate(result):
  print(f'{idx}: {l11}{l12} {l21}{l22}')

This outputs:

0: A1 B2
1: A1 C2
2: B1 A2
3: B1 C2
4: C1 A2
5: C1 B2
4
votes

Answering the question "given two lists, find all possible permutations of pairs of one item from each list" and using basic Python functionality (i.e., without itertools) and, hence, making it easy to replicate for other programming languages:

def rec(a, b, ll, size):
    ret = []
    for i,e in enumerate(a):
        for j,f in enumerate(b):
            l = [e+f]
            new_l = rec(a[i+1:], b[:j]+b[j+1:], ll, size)
            if not new_l:
                ret.append(l)
            for k in new_l:
                l_k = l + k
                ret.append(l_k)
                if len(l_k) == size:
                    ll.append(l_k)
    return ret

a = ['a','b','c']
b = ['1','2']
ll = []
rec(a,b,ll, min(len(a),len(b)))
print(ll)

Returns

[['a1', 'b2'], ['a1', 'c2'], ['a2', 'b1'], ['a2', 'c1'], ['b1', 'c2'], ['b2', 'c1']]