Reputation: 553
From my understanding of the intersection function, it finds complete overlap between elements in a list. For example:
tup_1 = [(1,2,3),(4,5,6)]
tup_2 = [(4,5,6)]
ol_tup = set(tup_1).intersection(tup_2)
print ol_tup
would yield:
set([(4, 5, 6)])
However, suppose my list of tuples are set up as this:
tup_1 = [(1,2,3),(4,5,5)]
tup_2 = [(4,5,6)]
Where there's an overlap in 2 elements of the 2nd tuple in tup_1 and 1st tuple in tup_2. If I want to python to return these 2 tuples: (4,5,5) and (4,5,6), is there an easier way than this nested for loop (below)?
for single_tuple_1 in tup_1:
for single_tuple_2 in tup_2:
if single_tuple_1[0] == single_tuple_2[0] and single_tuple_1[1] == single_tuple_2[1]:
print single_tuple_1,single_tuple_2
EDIT:
For this case, suppose order matters and suppose the tuples contain 5 elements:
tup_1 = [(1,2,3,4,5),(4,5,6,7,8),(11,12,13,14,15)]
tup_2 = [(1,2,3,4,8),(4,5,1,7,8),(11,12,13,14,-5)]
And I would like to find the tuples that intersect with each other in their respective first 4 elements. So the result should be:
[(1,2,3,4,5),(1,2,3,4,8),(11,12,13,14,15),(11,12,13,14,-5)]
How would the code change to accommodate this?
Upvotes: 2
Views: 2061
Reputation: 82929
If you want to return all the pairs of "overlapping" tuples there's no way around comparing all the pairs, i.e. a quadratic algorithm. But you could make the code a bit more elegant using a list comprehension, product
for the combinations and zip
and sum
for the comparison:
>>> tup_1 = [(1,2,3),(4,5,5),(7,8,9)]
>>> tup_2 = [(4,5,6),(0,5,5),(9,8,7)]
>>> [(a, b) for (a, b) in itertools.product(tup_1, tup_2)
... if sum(1 for ai, bi in zip(a, b) if ai == bi) >= 2]
[((4, 5, 5), (4, 5, 6)), ((4, 5, 5), (0, 5, 5))]
Note: This checks whether two tuples have the same element in at least two positions, i.e. order matters. If order should not matter, you can convert a
and b
to set
instead and check the size of their intersection, but that might fail for repeated numbers, i.e. the intersection of (1,1,2)
and (1,1,3)
would just be 1
instead of 2
.
If you only want to match the first two, or first two and last two elements, you can compare slices of the tuples in an accordant disjunction:
>>> [(a, b) for (a, b) in itertools.product(tup_1, tup_2)
... if a[:2] == b[:2]]
[((4, 5, 5), (4, 5, 6))]
>>> [(a, b) for (a, b) in itertools.product(tup_1, tup_2)
... if a[:2] == b[:2] or a[-2:] == b[-2:]]
[((4, 5, 5), (4, 5, 6)), ((4, 5, 5), (0, 5, 5))]
Upvotes: 3
Reputation: 164773
This is one way using a list comprehension. The logic as written checks for an overlap of at least 2 elements.
Note that if there is no overlap you will be left with the one element of tup_2
, but that can be trivially identified.
from itertools import chain
tup_1 = [(1,2,3),(4,5,5)]
tup_2 = [(4,5,6)]
y = sorted(tup_2[0])
res = [i for i in chain(tup_1, tup_2) if
sum(i==j for i, j in zip(sorted(i), y)) > 1]
print res
[(4, 5, 5), (4, 5, 6)]
Upvotes: 2