create a python list with combinations of the first element with the rest of list without repeat tuples in a combination

Question:

I’m trying to create a list of tuples in python with the combinations of first element of list with the rest elements of the list but without repeat the first element of the tuple in final result of combinations

I use this:

lst = [(2, 'ok'), (3, 'ok'), (3, 'fail'), (3, 'error'), (4, 'ok'), (4, 'fail'), (4, 'error')]

data = [[lst[0]] + list(i) for i in itertools.combinations(lst[1:], 2)]
for i in data:
    print(i)

and get this:

[(2, 'ok'), (3, 'ok'), (3, 'ok')]
[(2, 'ok'), (3, 'ok'), (3, 'fail')]
[(2, 'ok'), (3, 'ok'), (3, 'error')]
[(2, 'ok'), (3, 'ok'), (4, 'ok')]
[(2, 'ok'), (3, 'ok'), (4, 'fail')]
[(2, 'ok'), (3, 'ok'), (4, 'error')]
[(2, 'ok'), (3, 'fail'), (3, 'fail')]
[(2, 'ok'), (3, 'fail'), (3, 'error')]
[(2, 'ok'), (3, 'fail'), (4, 'ok')]
[(2, 'ok'), (3, 'fail'), (4, 'fail')]
[(2, 'ok'), (3, 'fail'), (4, 'error')]
[(2, 'ok'), (3, 'error'), (3, 'error')]
[(2, 'ok'), (3, 'error'), (4, 'ok')]
[(2, 'ok'), (3, 'error'), (4, 'fail')]
[(2, 'ok'), (3, 'error'), (4, 'error')]
[(2, 'ok'), (4, 'ok'), (4, 'ok')]
[(2, 'ok'), (4, 'ok'), (4, 'fail')]
[(2, 'ok'), (4, 'ok'), (4, 'error')]
[(2, 'ok'), (4, 'fail'), (4, 'fail')]
[(2, 'ok'), (4, 'fail'), (4, 'error')]
[(2, 'ok'), (4, 'error'), (4, 'error')]

but y want this:

[(2, 'ok'), (3, 'ok'), (4, 'ok')]
[(2, 'ok'), (3, 'ok'), (4, 'fail')]
[(2, 'ok'), (3, 'ok'), (4, 'error')]
[(2, 'ok'), (3, 'fail'), (4, 'ok')]
[(2, 'ok'), (3, 'fail'), (4, 'fail')]
[(2, 'ok'), (3, 'fail'), (4, 'error')]
[(2, 'ok'), (3, 'error'), (4, 'ok')]
[(2, 'ok'), (3, 'error'), (4, 'fail')]
[(2, 'ok'), (3, 'error'), (4, 'error')]

Thanks in advance!

PS: The len of the original lst can be > 1000

lst = [(2, 'ok'), (3, 'ok'), (3, 'fail'), (3, 'error'), .... (n, 'ok'), (n, 'fail'), (n, 'error')]
Asked By: xarc

||

Answers:

Let’s break down what you need here in a few steps.

Firstly, you will need to create all the combinations from the list of tuples.

from itertools import combinations

allcomb = combinations(lst,3)

Next, you will need to filter the combinations in which the first element has a two in it.

first = [i for i in allcomb if (2, 'ok') in i]

which is the same result as what you have tried. Finally, you will need to filter out the combinations where the first element in the tuple is distinct from one another.

second = [i for i in first if len(set(list(zip(*i))[0])) == 3]

[((2, 'ok'), (3, 'ok'), (4, 'ok')),
 ((2, 'ok'), (3, 'ok'), (4, 'fail')),
 ((2, 'ok'), (3, 'ok'), (4, 'error')),
 ((2, 'ok'), (3, 'fail'), (4, 'ok')),
 ((2, 'ok'), (3, 'fail'), (4, 'fail')),
 ((2, 'ok'), (3, 'fail'), (4, 'error')),
 ((2, 'ok'), (3, 'error'), (4, 'ok')),
 ((2, 'ok'), (3, 'error'), (4, 'fail')),
 ((2, 'ok'), (3, 'error'), (4, 'error'))]

You could group items with the same number and later use itertool.product()

import itertools

lst = [(2, 'ok'), (3, 'ok'), (3, 'fail'), (3, 'error'), (4, 'ok'), (4, 'fail'), (4, 'error')]

groups = {}

for number, word in lst:
    if number not in groups:
        groups[number] = []
    groups[number].append( (number, word) )

data = groups.values()

for item in itertools.product(*data):
    print(item)

Result:

((2, 'ok'), (3, 'ok'), (4, 'ok'))
((2, 'ok'), (3, 'ok'), (4, 'fail'))
((2, 'ok'), (3, 'ok'), (4, 'error'))
((2, 'ok'), (3, 'fail'), (4, 'ok'))
((2, 'ok'), (3, 'fail'), (4, 'fail'))
((2, 'ok'), (3, 'fail'), (4, 'error'))
((2, 'ok'), (3, 'error'), (4, 'ok'))
((2, 'ok'), (3, 'error'), (4, 'fail'))
((2, 'ok'), (3, 'error'), (4, 'error'))

EDIT:

You may also use itertools.groupby(lst, lambda x:x[0]) to group items.

import itertools

lst = [(2, 'ok'), (3, 'ok'), (3, 'fail'), (3, 'error'), (4, 'ok'), (4, 'fail'), (4, 'error')]

groups = itertools.groupby(lst, lambda x:x[0])

data = [list(val) for key, val in groups]
#keys, data = zip(*groups)  # ???
 
for item in itertools.product(*data):
    print(item)
Answered By: furas