fabiomaia
fabiomaia

Reputation: 602

Combinations of two non-consecutive items

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

Answers (4)

Stefan Pochmann
Stefan Pochmann

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

Abdou
Abdou

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

Stefan Pochmann
Stefan Pochmann

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

Rory Daulton
Rory Daulton

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

Related Questions