Jakob
Jakob

Reputation: 13

Permutate two lists but keep index

I want to permutate over all possible combinations of two lists, but I want that each element remains at its index. Here is an example of what I want to achieve.

[1, 2, 3] and [a, b, c] should give: [1, 2, 3], [a, 2, 3], [1, b, 3], [1, 2, c], [a, b, 3], etc.

Is there a neat way to achieve this result?

A normal permutation switches the place of in elements of the list, which is what I want to avoid.

Upvotes: 1

Views: 104

Answers (4)

chrslg
chrslg

Reputation: 13511

A one-liner

[[l2[k] if (2**k&i) else l1[k] for k in range(len(l1))] for i in range(2**len(l1))]

Principle is to use binary number i (I mean interpreted as binary; number have no base, it is how we choose to understand/print/input them that have base), as a set of choices among l1 or l2.

For example, if i is 6, that is 110 in binary, or 011 in reverse order (easier to create the test, and change nothing to the overall result), then we will choose in l1, then l2, then l2. So

i=6
[l2[k] if (2**k&i) else l1[k] for k in range(len(l1))]

is [1, 'b', 'c'].

Trick is in the test 2**k&i which is true if kth bit of i is 1.

We just have to do that for all possible i, that is all possible set of binary choice. Since there are len(l1) element to choose, that is 2**len(l1) different i combination.

Upvotes: 2

Larry the Llama
Larry the Llama

Reputation: 1090

Recursion

The amount of outputs we get is 2^n because we can either include a value, or its index. So, this can be done recursively:

def Recursive(index, value, i):
    if i < len(index):
        out = []
        prev = Recursive(index, value, i+1)
        for val in prev:
            out.append([index[i]] + val)
            out.append([value[i]] + val)
        return out
    else:
        return [[]]

Running your example:

print(Recursive([1,2,3],['a','b','c'], 0))

Output:

[[1, 2, 3], ['a', 2, 3], [1, 'b', 3], ['a', 'b', 3], [1, 2, 'c'], ['a', 2, 'c'], [1, 'b', 'c'], ['a', 'b', 'c']]

Alternatively

Using binary numbers to represent whether on not we can use a number:

value = ['a','b','c']
result = []
for i in range(2**len(value)):
    val = str(bin(i))[2:].zfill(len(value))
    result.append([[x+1,value[x]][int(val[x])] for x in range(len(val))])
print(result)

Upvotes: 0

mozway
mozway

Reputation: 262359

Using itertools.product and zip:

from itertools import product

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

out = list(product(*zip(list1, list2)))

Output:

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

If the order is important:

out = [list(x[::-1]) for x in product(*zip(list1[::-1], list2[::-1]))]

Output:


[[1, 2, 3],
 ['a', 2, 3],
 [1, 'b', 3],
 ['a', 'b', 3],
 [1, 2, 'c'],
 ['a', 2, 'c'],
 [1, 'b', 'c'],
 ['a', 'b', 'c']]

Upvotes: 4

zack Zacharias
zack Zacharias

Reputation: 1

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

combinations = []
for i in range(len(list1)):
  for j in range(len(list2)):
    if i == j:
      combination = list1
    else:
      combination = list1[:i] + [list2[j]] + list1[i+1:]
    combinations.append(combination)

print(combinations)

Upvotes: 0

Related Questions