Home > Software engineering >  Permutate two lists but keep index
Permutate two lists but keep index

Time:01-08

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.

CodePudding user response:

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']]

CodePudding user response:

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.

CodePudding user response:

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)

CodePudding user response:

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)
  • Related