Reputation: 602
Given a list, how can I get all combinations between two non-consecutive items?
For example, for input [1, 2, 3, 4, 5]
how can I get the output [(1,3), (1,4), (1,5), (2,4), (2,5), (3,5)]
?
I'm not interested in (1,2)
, (2,3)
, (3,4)
or (4,5)
because they are consecutive (i.e. next to each other) in the list, but everything else I'm interested.
What's the most idiomatic way to do this in Python?
Upvotes: 6
Views: 818
Reputation: 28656
Here's a generalized solution for r-length combinations avoiding consecutive elements. It gets all index combinations for an appropriately shorter list and then spreads out each combination's indexes. For example for r=3, any combination (x,y,z) turns into used indexes x+0, y+1, z+2.
from itertools import combinations
def non_consecutive_combinations(lst, r):
return [tuple(lst[j+i] for i, j in enumerate(combi))
for combi in combinations(range(len(lst)-r+1), r)]
Demo for r=2:
>>> non_consecutive_combinations([1, 2, 3, 4, 5], 2)
[(1, 3), (1, 4), (1, 5), (2, 4), (2, 5), (3, 5)]
Demo for r=3:
>>> non_consecutive_combinations([1, 2, 3, 4, 5, 6, 7], 3)
[(1, 3, 5), (1, 3, 6), (1, 3, 7), (1, 4, 6), (1, 4, 7), (1, 5, 7), (2, 4, 6), (2, 4, 7), (2, 5, 7), (3, 5, 7)]
A simplified version just for pairs:
>>> [(lst[i], lst[j+1]) for i, j in combinations(range(len(lst)-1), 2)]
[(1, 3), (1, 4), (1, 5), (2, 4), (2, 5), (3, 5)]
Upvotes: 2
Reputation: 13304
If you are interested in the combinations with non-consecutive numbers from the list:
from itertools import combinations
lst = [1, 2, 3, 4, 5]
list(filter(lambda x: lst.index(x[1]) - lst.index(x[0]) > 1, combinations(lst,2)))
[(1, 3), (1, 4), (1, 5), (2, 4), (2, 5), (3, 5)]
This compares the indices of the two numbers in a given combination and makes sure that the difference of their indices is greater than 1.
I hope it helps.
Upvotes: 2
Reputation: 28656
A simple list comprehension:
>>> lst = [1, 2, 3, 4, 5]
>>> [(a, b) for i, a in enumerate(lst) for b in lst[i+2:]]
[(1, 3), (1, 4), (1, 5), (2, 4), (2, 5), (3, 5)]
Upvotes: 5
Reputation: 22564
Here is a fairly "idiomatic" way not using any modules, which is more efficient than some other implementations since every trip through the loop is used--no rejected possibilities.
r = [1, 2, 3, 4, 5]
c = [(r[i], r[j]) for i in range(len(r)-2) for j in range(i+2, len(r))]
print(c)
This gives
[(1, 3), (1, 4), (1, 5), (2, 4), (2, 5), (3, 5)]
It is not completely idiomatic, since it loops on indices rather than values, but your restriction regarding the positions in the list rather than the values makes that fairly necessary. If you want a generator rather than a list, replace the outer brackets with parentheses.
Upvotes: 2