Genealogy-Relationship
view release on metacpan or search on metacpan
lib/Genealogy/Relationship.pm view on Meta::CPAN
my ($person1, $person2) = @_;
# If the two people are the same person, then return (0, 0).
return (0, 0)
if $person1->$identifier_field_name eq $person2->$identifier_field_name;
my $map1 = $self->_ancestor_map($person1);
my $map2 = $self->_ancestor_map($person2);
my ($best_i, $best_j, $best_total);
for my $id (keys %$map1) {
if (exists $map2->{$id}) {
my $i = $map1->{$id}{distance};
my $j = $map2->{$id}{distance};
my $total = $i + $j;
if (!defined $best_total || $total < $best_total) {
$best_total = $total;
$best_i = $i;
$best_j = $j;
}
}
}
die "Can't work out the relationship.\n" unless defined $best_total;
return ($best_i, $best_j);
}
=head2 get_relationship_ancestors
Given two people, returns lists of people linking those two people
to their most recent common ancestor.
The return value is a reference to an array containing two array
references. The first referenced array contains the person1 and
all their ancestors up to and including the most recent common
ancestor. The second list does the same for person2.
When a person has two parents, the shortest path to the common ancestor
is used.
=cut
method get_relationship_ancestors {
my ($person1, $person2) = @_;
my $mrca = $self->most_recent_common_ancestor($person1, $person2)
or die "There is no most recent common ancestor\n";
return [
$self->_path_to_ancestor($person1, $mrca),
$self->_path_to_ancestor($person2, $mrca),
];
}
=head2 _path_to_ancestor
Internal method. Given a person object and a target ancestor object, returns
an array reference containing the shortest path from the person to the
ancestor (inclusive of both endpoints). Uses breadth-first search so that
the shortest path is always found, even when a person has two parents.
=cut
method _path_to_ancestor {
my ($person, $target) = @_;
my $target_id = $target->$identifier_field_name;
my $person_id = $person->$identifier_field_name;
return [$person] if $person_id eq $target_id;
# BFS to find the shortest path
my @queue = ([$person]);
my %visited = ($person_id => 1);
while (@queue) {
my $path = shift @queue;
my $current = $path->[-1];
for my $parent ($self->_get_parents($current)) {
my $parent_id = $parent->$identifier_field_name;
next if $visited{$parent_id}++;
my $new_path = [@$path, $parent];
return $new_path if $parent_id eq $target_id;
push @queue, $new_path;
}
}
die "No path found to ancestor\n";
}
=head1 AUTHOR
Dave Cross <dave@perlhacks.com>
=head1 SEE ALSO
perl(1)
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2018-2026, Magnum Solutions Ltd. All Rights Reserved.
This script is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut
1;
( run in 1.437 second using v1.01-cache-2.11-cpan-9789f410c06 )