Last active
October 23, 2023 18:19
-
-
Save Plutanium/2c70708d2c26b87295707571c6a52b62 to your computer and use it in GitHub Desktop.
Person
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type PartialPick<T, F extends keyof T> = Omit<T, F> & Partial<Pick<T, F>>; | |
type PersonParams = { | |
firstName: string, | |
lastName: string, | |
birthDate?: Date | |
gender: Gender | |
mother?: Person | PersonParams | |
fatherOrMother?: Person | PersonParams | |
} | |
export class Gender { | |
readonly gender; | |
private constructor(gender: string) { | |
this.gender = gender; | |
} | |
static MALE = new this("male"); | |
static FEMALE = new this("female"); | |
} | |
Object.freeze(Gender.MALE); | |
Object.freeze(Gender.FEMALE); | |
export default class Person { | |
firstName: string; | |
lastName: string; | |
gender: Gender; | |
birthDate: Date | undefined; | |
#mother: Person | undefined; | |
#fatherOrMother: Person | undefined; | |
#children: Person[] = []; | |
constructor(p: PersonParams) { | |
this.firstName = p.firstName; | |
this.lastName = p.lastName; | |
this.birthDate = p.birthDate; | |
this.gender = p.gender; | |
this.mother = p.mother; | |
this.fatherOrMother = p.fatherOrMother; | |
this.birthDate = p.birthdate; | |
} | |
get age(): number { | |
if (!this.birthDate) { | |
throw new Error("Missing birthdate"); | |
} | |
const now = new Date(); | |
const yearDiff = now.getFullYear() - this.birthDate.getFullYear(); | |
if (now.getMonth() > this.birthDate.getMonth()) { | |
return yearDiff; | |
} | |
if (now.getMonth() < this.birthDate.getMonth()) { | |
return yearDiff - 1; | |
} | |
if (now.getDate() >= this.birthDate.getDate()) { | |
return yearDiff; | |
} | |
return yearDiff - 1; | |
} | |
get aunts(): Person[] { | |
return this.getParentsSiblings(Gender.FEMALE); | |
} | |
set brothers(brothers: Person | PersonParams | Array<Person | PersonParams> | undefined) { | |
this.#setSiblings(brothers, Gender.MALE); | |
} | |
get brothers(): Person[] { | |
return this.siblings.filter(sibling => sibling.gender === Gender.MALE); | |
} | |
set children(children: Person | PersonParams | Array<Person | PersonParams> | undefined) { | |
if (!children) { | |
this.#children = []; | |
return; | |
} | |
if (!Array.isArray(children)) { | |
children = [children]; | |
} | |
children = (children as Array<Person | PersonParams>).filter(child => child !== this); | |
children = [... new Set(children)]; | |
this.#children = []; | |
children.forEach(child => this.addChild(child)); | |
} | |
get children(): Person[] { | |
return Array.from(this.#children); | |
} | |
get cousins(): Person[] { | |
const cousins = this.uncles.map(uncle => uncle.#children) | |
.concat(this.aunts.map(aunt => aunt.#children)) | |
.flat() | |
.filter(cousin => cousin !== this); | |
return Array.from(new Set(cousins)); | |
} | |
set daughters(daughters: Person | PersonParams | Array<Person | PersonParams> | undefined) { | |
this.#children = this.sons; | |
if (!daughters) { | |
return; | |
} | |
if (!Array.isArray(daughters)) { | |
daughters = [daughters]; | |
} | |
(daughters as Array<Person | PersonParams>) | |
.filter(child => child.gender === Gender.FEMALE) | |
.forEach(daughter => this.addChild(daughter)); | |
} | |
get daughters(): Person[] { | |
return this.#children.filter(child => child.gender === Gender.FEMALE); | |
} | |
set fatherOrMother(fatherOrMother: Person | PersonParams | undefined) { | |
if (!fatherOrMother) { | |
this.#fatherOrMother = undefined; | |
return; | |
} | |
if (!(fatherOrMother instanceof Person)) { | |
fatherOrMother = new Person(fatherOrMother); | |
} | |
this.#updateParent(fatherOrMother as Person, fatherOrMother.gender); | |
this.#fatherOrMother = fatherOrMother as Person; | |
} | |
get fatherOrMother(): Person | undefined { | |
return this.#fatherOrMother; | |
} | |
set fullBrothers(fullBrothers: Person | PersonParams | Array<Person | PersonParams> | undefined) { | |
this.#setFullSiblings(fullBrothers, Gender.MALE); | |
} | |
get fullBrothers(): Person[] { | |
return this.fullSiblings.filter(sibling => sibling.gender === Gender.MALE); | |
} | |
set fullSiblings(fullSiblings: Person | PersonParams | Array<Person | PersonParams> | undefined) { | |
if (!(this.#fatherOrMother && this.#mother)) { | |
throw new Error("Cannot set full siblings if both parents aren't present"); | |
} | |
if (!fullSiblings) { | |
fullSiblings = []; | |
} | |
else if (!Array.isArray(fullSiblings)) { | |
fullSiblings = [fullSiblings]; | |
} | |
const motherOnlyChildren = this.#mother.#children.filter(child => !this.#fatherOrMother!.#children.includes(child)); | |
const fatherOnlyChildren = this.#fatherOrMother.#children.filter(child => !this.#mother!.#children.includes(child)); | |
this.#mother.children = motherOnlyChildren; | |
this.#fatherOrMother.children = fatherOnlyChildren; | |
fullSiblings.forEach(sibling => this.addSibling(sibling)); | |
} | |
get fullSiblings(): Person[] { | |
if (!(this.#fatherOrMother && this.#mother)) { | |
throw new Error("Cannot get full siblings if both parents aren't present"); | |
} | |
return this.#mother.#children.filter(child => this.#fatherOrMother!.#children.includes(child) && child !== this); | |
} | |
set name(name: string) { | |
const [firstName, lastName] = name.split(" "); | |
if(!firstName || !lastName) { | |
throw new Error("name argument must be of the format {firstName} {lastName}"); | |
} | |
this.firstName = firstName; | |
this.lastName = lastName; | |
} | |
get name(): string { | |
return this.firstName + " " + this.lastName | |
} | |
get nephews(): Person[] { | |
return this.getSiblingsChildren(Gender.MALE); | |
} | |
get nieces(): Person[] { | |
return this.getSiblingsChildren(Gender.FEMALE); | |
} | |
set mother(mother: Person | PersonParams | undefined) { | |
if (!mother) { | |
this.#mother = undefined; | |
return; | |
} | |
if (!(mother instanceof Person)) { | |
mother = new Person(mother); | |
} | |
this.#updateParent((mother as Person), Gender.FEMALE); | |
this.#mother = mother as Person; | |
} | |
get mother(): Person | undefined { | |
return this.#mother; | |
} | |
set fullSisters(fullSisters: Person | PersonParams | Array<Person | PersonParams> | undefined) { | |
this.#setFullSiblings(fullSisters, Gender.FEMALE); | |
} | |
get fullSisters(): Person[] { | |
return this.fullSiblings.filter(sibling => sibling.gender === Gender.FEMALE); | |
} | |
get grandFathers(): Person[] { | |
return this.#getGrandParents(Gender.MALE); | |
} | |
get grandMothers(): Person[] { | |
return this.#getGrandParents(Gender.FEMALE); | |
} | |
get grandParents(): Person[] { | |
return this.#getGrandParents(); | |
} | |
get halfBrothers(): Person[] { | |
return this.halfSiblings.filter(sibling => sibling.gender === Gender.MALE); | |
} | |
get halfSisters(): Person[] { | |
return this.halfSiblings.filter(sibling => sibling.gender === Gender.FEMALE); | |
} | |
get halfSiblings(): Person[] { | |
if (!(this.#fatherOrMother && this.#mother)) { | |
throw Error("Cannot get half siblings if both parents aren't present"); | |
} | |
const fatherOnlyChildren = this.#fatherOrMother.#children.filter(child => !this.#mother!.#children.includes(child)); | |
const motherOnlyChildren = this.#mother.#children.filter(child => !this.#fatherOrMother!.#children.includes(child)); | |
return fatherOnlyChildren.concat(motherOnlyChildren); | |
} | |
set siblings(siblings: Person | PersonParams | Array<Person | PersonParams> | undefined) { | |
if (!this.#fatherOrMother && !this.#mother) { | |
throw new Error("Cannot set siblings if no parent is present"); | |
} | |
if (this.#fatherOrMother) { | |
this.#fatherOrMother.children = []; | |
} | |
if (this.#mother) { | |
this.#mother.children = []; | |
} | |
if(!siblings) { | |
siblings = []; | |
} | |
else if (!Array.isArray(siblings)) { | |
siblings = [siblings]; | |
} | |
siblings = (siblings as Array<Person | PersonParams>).filter(sibling => sibling !== this); | |
siblings = [...new Set(siblings), this]; | |
if (this.#fatherOrMother) { | |
this.#fatherOrMother.children = siblings; | |
} | |
if (this.#mother) { | |
this.#mother.children = siblings; | |
} | |
} | |
get siblings(): Person[] { | |
if (!this.#mother && !this.#fatherOrMother) { | |
return []; | |
} | |
if (!this.#fatherOrMother && this.#mother || this.#fatherOrMother && !this.#mother) { | |
return (this.#mother || this.#fatherOrMother)!.#children | |
.filter(child => child !== this); | |
} | |
const motherChildren = this.#mother!.#children.filter(child => child !== this); | |
const fatherOrMotherChildren = this.#fatherOrMother!.#children.filter(child => child !== this && !motherChildren.includes(child)); | |
return motherChildren.concat(fatherOrMotherChildren); | |
} | |
get siblingsChildren(): Person[] { | |
return this.getSiblingsChildren(); | |
} | |
set sisters(sisters: Person | PersonParams | Array<Person | PersonParams> | undefined) { | |
this.#setSiblings(sisters, Gender.FEMALE); | |
} | |
get sisters(): Person[] { | |
return this.siblings.filter(sibling => sibling.gender === Gender.FEMALE); | |
} | |
set sons(sons: Person | PersonParams | Array<Person | PersonParams> | undefined) { | |
this.#children = this.daughters; | |
if (!sons) { | |
return; | |
} | |
if (!Array.isArray(sons)) { | |
sons = [sons]; | |
} | |
(sons as Array<Person | PersonParams>) | |
.filter(child => child.gender === Gender.MALE) | |
.forEach(son => this.addChild(son)); | |
} | |
get sons(): Person[] { | |
return <Person[]>this.#children.filter(child => child.gender === Gender.MALE); | |
} | |
get uncles(): Person[] { | |
return this.getParentsSiblings(Gender.MALE); | |
} | |
addChild(child: Person | PersonParams): Person { | |
if (!(child instanceof Person)) { | |
child = new Person(child); | |
} | |
if(this.gender === Gender.MALE) { | |
child.fatherOrMother = this; | |
} else { | |
if(!child.mother) { | |
child.mother = this; | |
} else { | |
child.fatherOrMother = this; | |
} | |
} | |
return child as Person; | |
} | |
addSibling(sibling: Person | PersonParams): Person { | |
if (!(sibling instanceof Person)) { | |
sibling = new Person(sibling); | |
} | |
if (this === sibling) { | |
throw new Error("You cannot be your own sibling"); | |
} | |
if (!this.#fatherOrMother && !this.#mother) { | |
throw new Error("Cannot add sibling if both parents are not present"); | |
} | |
if (this.fatherOrMother) { | |
sibling.fatherOrMother = this.#fatherOrMother; | |
} | |
if (this.mother) { | |
sibling.mother = this.#mother; | |
} | |
return sibling as Person; | |
} | |
addBrother(brother: Person | PersonParams): Person { | |
return this.#addSibling(brother, Gender.MALE); | |
} | |
addSister(sister: Person | PersonParams): Person { | |
return this.#addSibling(sister, Gender.FEMALE); | |
} | |
isSibling(person: Person): boolean { | |
if(this === person) { | |
return false; | |
} | |
return this.siblings.includes(person); | |
} | |
getCousins(gender?: Gender): Person[] { | |
return gender | |
? this.cousins.filter(cousin => cousin.gender === gender) | |
: this.cousins; | |
} | |
getSiblingsChildren(gender?: Gender): Person[] { | |
let children = this.siblings | |
.map(sibling => sibling.#children) | |
.flat(); | |
if (gender) { | |
children = children.filter(child => child.gender === gender); | |
} | |
return Array.from(new Set(children)); | |
} | |
getParentsSiblings(gender?: Gender): Person[] { | |
let siblings = (this.#mother?.siblings || []).concat(this.#fatherOrMother?.siblings || []); | |
if (gender) { | |
siblings = siblings.filter(sibling => sibling.gender === gender); | |
} | |
return Array.from(new Set(siblings)); | |
} | |
hasYuriParents() { | |
return this.mother && this.fatherOrMother?.gender === Gender.FEMALE; | |
} | |
makeChildWith(person: Person | PersonParams, child: Person|PartialPick<PersonParams,"lastName"|"gender">) { | |
if(this.gender === Gender.MALE && person.gender === Gender.MALE) { | |
throw new Error("Cannot make a child without a female parent"); | |
} | |
if(!(person instanceof Person)) { | |
person = new Person(person); | |
} | |
if(this.gender === Gender.FEMALE && person.gender === Gender.FEMALE) { | |
child.gender = Gender.FEMALE; | |
} else if(!child.gender) { | |
child.gender = Math.random() > 0.5 ? Gender.FEMALE : Gender.MALE; | |
} | |
if(!child.lastName) { | |
child.lastName = Math.random() > 0.5 ? this.lastName : person.lastName; | |
} | |
if(!(child instanceof Person)) { | |
child = new Person(child as PersonParams); | |
} | |
this.addChild(child as Person); | |
(person as Person).addChild(child as Person); | |
return child as Person; | |
} | |
static makeMale(params: Omit<PersonParams,"gender">) { | |
return new Person({...params, gender: Gender.MALE}); | |
} | |
static makeFemale(params: Omit<PersonParams,"gender">) { | |
return new Person({...params, gender: Gender.FEMALE}); | |
} | |
#addSibling(sibling: Person | PersonParams, gender: Gender): Person { | |
if (!sibling) { | |
throw new TypeError("null or undefined value is not permitted"); | |
} | |
if (!(sibling instanceof Person)) { | |
sibling = new Person(sibling); | |
} | |
sibling.gender = sibling.gender || gender; | |
if (sibling.gender !== gender) { | |
throw new Error("This sibling must be of the gender: " + gender); | |
} | |
return this.addSibling(sibling); | |
} | |
#getGrandParents(gender?: Gender): Person[] { | |
const grandParents: Array<Person|undefined> = []; | |
if (gender) { | |
const expectedParent = gender === Gender.MALE ? "fatherOrMother" : "mother"; | |
if (this.#mother) { | |
grandParents.push(this.#mother[expectedParent]); | |
} | |
if (this.#fatherOrMother) { | |
grandParents.push(this.#fatherOrMother[expectedParent]); | |
} | |
} else { | |
if (this.#mother) { | |
grandParents.push(this.#mother.#fatherOrMother); | |
grandParents.push(this.#mother.#mother); | |
} | |
if (this.#fatherOrMother) { | |
grandParents.push(this.#fatherOrMother.#fatherOrMother); | |
grandParents.push(this.#fatherOrMother.#mother); | |
} | |
} | |
return Array.from(new Set(grandParents.filter(Boolean))) as Person[]; | |
} | |
#setFullSiblings(fullSiblings: Person | PersonParams | Array<Person | PersonParams> | undefined, gender: Gender) { | |
if (!(this.#fatherOrMother && this.#mother)) { | |
throw new Error("Cannot set full siblings if both parents aren't present"); | |
} | |
if (!fullSiblings) { | |
fullSiblings = []; | |
} | |
if (!Array.isArray(fullSiblings)) { | |
fullSiblings = [fullSiblings]; | |
} | |
this.fullSiblings = gender === Gender.MALE ? this.fullBrothers : this.fullSisters; | |
fullSiblings.forEach(sibling => this.addSibling(sibling)); | |
} | |
#setSiblings(siblings: Person | PersonParams | Array<Person | PersonParams> | undefined, gender: Gender) { | |
if (!siblings) { | |
siblings = []; | |
} | |
if (!Array.isArray(siblings)) { | |
siblings = [siblings]; | |
} | |
this.siblings = gender === Gender.MALE ? | |
this.sisters : | |
this.brothers; | |
siblings.forEach(sibling => this.addSibling(sibling)); | |
} | |
#updateParent(parent: Person, gender: Gender) { | |
if (this === parent) { | |
throw new Error("Parent cannot be self"); | |
} | |
if (parent.gender !== gender) { | |
throw new Error("Mismatch between parent gender and gender provided"); | |
} | |
if (!parent.#children.includes(this)) { | |
parent.#children.push(this); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment