I came up with 2 different solutions:
The first one I prefer is about linking each record with a parent. And then of course navigate further in the hierarchy until an element is mapped to itself.
So the code would be:
def build_mapping(input_pairs):
mapping = {}
for pair in input_pairs:
left = pair[0]
right = pair[1]
parent_left = None if left not in mapping else mapping[left]
parent_right = None if right not in mapping else mapping[right]
if parent_left is None and parent_right is None:
mapping[left] = left
mapping[right] = left
continue
if parent_left is not None and parent_right is not None:
if parent_left == parent_right:
continue
top_left_parent = mapping[parent_left]
top_right_parent = mapping[parent_right]
while top_left_parent != mapping[top_left_parent]:
mapping[left] = top_left_parent
top_left_parent = mapping[top_left_parent]
mapping[top_left_parent] = top_right_parent
mapping[left] = top_right_parent
continue
if parent_left is None:
mapping[left] = parent_right
else:
mapping[right] = parent_left
return mapping
def get_groups(input_pairs):
mapping = build_mapping(input_pairs)
groups = {}
for elt, parent in mapping.items():
if parent not in groups:
groups[parent] = set()
groups[parent].add(elt)
return list(groups.values())
So, with the following input:
groups = get_groups([('A', 'B'), ('A', 'C'), ('D', 'A'), ('E', 'F'),
('F', 'C'), ('G', 'H'), ('I', 'J'), ('K', 'L'),
('L', 'M'), ('M', 'N')])
We get:
[{'A', 'B', 'C', 'D', 'E', 'F'}, {'G', 'H'}, {'I', 'J'}, {'K', 'L', 'M', 'N'}]
The second maybe less efficient solution would be:
def get_groups_second_method(input_pairs):
groups = []
for pair in input_pairs:
left = pair[0]
right = pair[1]
left_group = None
right_group = None
for i in range(0, len(groups)):
group = groups[i]
if left in group:
left_group = (group, i)
if right in group:
right_group = (group, i)
if left_group is not None and right_group is not None:
merged = right_group[0].union(left_group[0])
groups[right_group[1]] = merged
groups.pop(left_group[1])
continue
if left_group is None and right_group is None:
new_group = {left, right}
groups.append(new_group)
continue
if left_group is None:
right_group[0].add(left)
else:
left_group[0].add(right)
return groups