NAME

Genealogy::Relationship - calculate the relationship between two people

SYNOPSIS

use Genealogy::Relationship;
use Person; # Imaginary class modelling people

my $rel = Genealogy::Relationship->new;

my $grandfather = Person->new( ... );
my $father      = Person->new( ... );
my $me          = Person->new( ... );
my $aunt        = Person->new( ... );
my $cousin      = Person->new( ... );

my $common_ancestor = $rel->most_recent_common_ancestor(
  $me, $cousin,
);
say $common_ancestor->name; # Grandfather's name

say $rel->get_relationship($me, $grandfather); # Grandson
say $rel->get_relationship($grandfather, $me); # Grandfather

say $rel->get_relationship($father, $cousin);  # Uncle
say $rel->get_relationship($cousin, $father);  # Niece

DESCRIPTION

This module makes it easy to calculate the relationship between two people.

If you have a set of objects modelling your family tree, then you will be able to use this module to get a description of the relationship between any two people on that tree.

The objects that you use with this module need to implement three methods:

  • parents

    This method should return an array reference containing the objects which are the parents of the current person. The array reference can contain zero, one or two objects.

    If an object does not have a parents() method, then the module will fall back to using a parent() method that returns a single parent object.

  • id

    This method should return a unique identifier for the current person. The identifier can be a string or a number.

  • gender

    This method should return the gender of the current person. It should be the character 'm' or 'f'.

Note

THe objects that you use with this class can actually have different names for these methods. parent, parents, id and gender are the default names used by this module, but you can change them by passing the correct names to the constructor. For example:

my $rel = Genealogy::Relationship->new(
  parent_field_name     => 'progenitor',
  parents_field_name    => 'progenitors',
  identifier_field_name => 'person_id',
  gender_field_name     => 'sex',
);

Limitations

This module was born out of a need I had while creating https://lineofsuccession.co.uk/. Relationship calculations are based on finding the most recent common ancestor between two people, and choosing the path that uses the fewest generations.

Constructor

The constructor for this class takes one optional attribute called `abbr`. The default value for this attribute is 2. When set, strings of repeated "great"s in a relationship description will collapsed to "$n x great".

For example, if the description you have is "Great, great, great grandfather", then that will be abbreviated to to "3 x great grandfather".

The value for `abbr` is the maximum number of repetitions that will be left untouched. You can turn abbreviations off by setting `abbr` to zero.

Caching

Calculating relationship names isn't at all different. But there can be a lot of (simple and repetitive) work involved. This is particularly true if your objects are based on database tables (as I found to my expense).

If you're calculating a lot of relationships, then you should probably consider putting a caching layer in front of get_relationship.

Methods

The following methods are defined.

most_recent_common_ancestor

Given two person objects, returns the person who is the most recent common ancestor for the given people. When multiple common ancestors exist at the same distance, returns the one reachable via the fewest total generations across both people.

_get_parents

Internal method. Given a person object, returns a list of that person's parents. Uses the parents_field_name method if the person object supports it; otherwise falls back to the configured parent_field_name method.

_ancestor_map

Internal method. Given a person object, returns a hash reference mapping each ancestor's identifier to a hash containing distance (number of generations from the given person) and person (the ancestor object). The person themself is included at distance zero.

get_ancestors

Given a person object, returns a list of person objects, one for each ancestor of the given person. When a person has two parents, all ancestors from both parent lines are included (breadth-first order).

The first entries in the list will be the person's direct parent(s) and the last person will be their most distant ancestor.

get_relationship

Given two person objects, returns a string containing a description of the relationship between those two people.

abbr_rel

Optionally abbreviate a relationship description.

make_rel

Given relationship co-ords and a gender, this will synthesise a relationship description. This only works because we've hard-coded an initial relationship table that covers all of the trickier situations.

times_str

Given an integer, this method returns a string version for use in a "removed" cousin relationship, i.e. "once", "twice", "three times", etc.

get_relationship_coords

Given two person objects, returns the "co-ordinates" of the relationship between them.

The relationship co-ordinates are a pair of integers. The first integer is the number of generations between the first person and their most recent common ancestor. The second integer is the number of generations between the second person and their most recent common ancestor.

When a person has two parents, the shortest path to the common ancestor is used.

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.

_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.

AUTHOR

Dave Cross <dave@perlhacks.com>

SEE ALSO

perl(1)

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.