Reputation: 1621
I am trying to model family relationship as a graph so that I can query them to find associated relationship and others. This is for a practice exercise so I can't use existing solutions like graph databases and so on.
I'm trying to model something like this where an edge exists between related entities(Person) to signify a relationship.
This is how I got started.
public class Person
{
public string Name { get; set; }
List<IEdge> Children { get; set; }
IEdge Spouse { get; set; }
IEdge Father { get; set; }
IEdge Mother { get; set; }
}
public class Edge
{
Person From { get; set; }
Person To { get; set; }
public string RelationshipType { get; set; }
}
public class Family
{
Dictionary<string, Person> familyGraph = new Dictionary<string, Person>();
}
The adjacency list representation will be stored in a key, value pair and all the edges from a person will be stored in the corresponding Person node.
So adding a relationship with this is straightforward.
Now when it comes to retrieving relationship, like finding out siblings, Maternal Uncle and so on. I kinda have to manually navigate the edges to find the appropriate person for each type of relationship. And for each relationship, I'll have to do the same.
For example, for finding my niece, I'll have to traverse to my mom find my siblings and get their children,
for finding my mother in law. I have to traverse to my wife find her mum. I'm thinking this is how the code will look like with this data structure
List<string> FindNeice(string username)
{
currentPerson = GerPerson(username)
siblings = currentPerson.Mother.Children;
niece = siblings.Where(mbox => mbox.Gender == "F").SelectMany(m => m.Children);
}
So this has to be there for every relationship. Yes, some can be reused because Maternal/Paternal relationships are the same as starting person swapped between you and your wife.
I'm thinking if there is a better way to model this and a better way to write extraction relationships.
Upvotes: 2
Views: 2105
Reputation: 134025
Family relationships seem so simple, but they become complex in a hurry. You mention in-law relationships (you're wife's mother), but even closer relationships are complex. Consider siblings, for example. Technically:
And don't even get me started on adoptive relationships.
But let's put those complications aside for a moment and assume a world without step-brothers, half-sisters, etc: a simple family tree.
The most flexible way to model this is to have a Person
record that contains information about that person, but not about any relationships. That info would be, for example, name, date of birth, etc. And that person has a unique identifier that won't ever change. Say, a 64-bit number. You have a big table of Person
records.
You also have a big table of Relationship
records. Each record contains the Source, the Target, and the type of relationship. There are two types of relationships: Parent, and Spouse.
(I've purposely left gendered relationships out of this simple example because including it adds needless complexity, and the current social discussion about gender identity makes it even more complicated.)
So if your immediate family consists of you (George), your parents (Mary and Dave), and your two siblings (Bob and Sally), the relationships are:
Mary, George, Parent
Dave, George, Parent
Mary, Bob, Parent
Dave, Bob, Parent
Mary, Sally, Parent
Dave, Sally, Parent
Mary, Dave, Spouse
Dave, Mary, Spouse
Read that as "Mary is George's Parent."
Note that there is some debate about whether it's best to include the reciprocal spousal relationships. I've included them here because it's easier to reason about that way.
So if you want to find a person's siblings, you do this:
You can then choose whether you want to write code for the more complex relationships, or develop simple script-like definitions for those. Consider:
parents - intrinsic function
children - intrinsic function
spouse - intrinsic function
siblings - (parents children) (probably should be an intrinsic, to eliminate self)
grandparents - (parents parents)
uncles/aunts - (parents siblings)
cousins - (parents siblings children)
parents-in-law - (spouse parents)
siblings-in-law - (spouse siblings)
nieces/nephews - (siblings children) + (siblings-in-law children)
Given the parent/child and spouse/spouse relationships, you can easily script queries to find any other type of familial relationship. Writing the code to perform those queries is pretty easy, and you eliminate all kinds of problems you'll encounter if you try to hand-code them.
Coding it becomes a matter of writing the four intrinsic functions (Parents
, Children
, Siblings
, and Spouse
), each of which takes as a parameter an IEnumerable<PersonId>
and returns an IEnumerable<PersonId>
, and then combining those functions. The Siblings
function has to exclude from the result any values that were in the input parameter. Cousins becomes:
var person = new List<PersonId> {personId};
var cousins = person.Parents().Siblings().Children();
Writing the code to generate those queries from the simple query definitions I described isn't terribly difficult. Or, if you'd rather go with static relationships, you can write individual functions for each of the relationships.
Now, if you want to extend it to step-siblings, half-siblings, etc., you keep the same basic relationships and add more information like relationship sub-type. You'd still query for the basic parent/child relationship, but then filter out the "steps" and "halfs" or other sub-types if you want. For gendered relationships, just add a gender to the Person
record. For sister, query for siblings and filter the result to include only females.
Now if you want to extend your query definitions to include gender, it becomes something like:
brother - (siblings male)
grandmother - (parents parents female)
maternal-uncle - (parents female siblings male)
spouse-step-sister - (spouse siblings step female)
Upvotes: 6