Each main function returns a table containing a list of Ancestors or Descendants or All Relatives for a given root Individual. They operate by traversing the Family record links for Husband, Wife, and Children.
The first set of functions employ a looping method, and the second set employs recursion. The recursive version includes the Record Id in the tables.
It would be possible to call the built in functions IsAncestorOf or IsDescendantOf or IsRelativeOf or RelationPool, but they are slower in execution, especially if the given Individual is not the File Root.
Requires: None
Looping Method
-
--[[ @function: CheckDuplicate @description: Adds Record Id as value and index, to table if it does not already exist @parameters: Item Pointer - Must be at Record Level @returns: true if pointer is a duplicate @requires: none ]] function CheckDuplicate(table, ptr) local id = fhGetRecordId(ptr) if table[id] == nil then table[id] = id return false else return true end end -- End Function --[[ @function: GetAncestorList @description: Returns a list of Ancestors @parameters: Item Pointer - Must be at Record Level @returns: table of record Item Pointers @requires: CheckDuplicate ]] function GetAncestorList(ptr) local ancestorlist = {} local dupcheck = {} local ptrMother = fhNewItemPtr() local ptrFather = fhNewItemPtr() local ptrFamily = fhNewItemPtr() table.insert(ancestorlist,ptr:Clone()) CheckDuplicate(dupcheck,ptr) iLoop = 1 while iLoop <= #ancestorlist do local ptrBase = ancestorlist[iLoop] ptrFamily:MoveTo(ptrBase,'~.FAMC') while ptrFamily:IsNotNull() do ptrMother:MoveTo(fhGetValueAsLink(ptrFamily),'~.WIFE[1]>') if ptrMother:IsNotNull() and (not CheckDuplicate(dupcheck,ptrMother))then table.insert(ancestorlist,ptrMother:Clone()) end ptrMother:MoveTo(fhGetValueAsLink(ptrFamily),'~.WIFE[2]>') if ptrMother:IsNotNull() and (not CheckDuplicate(dupcheck,ptrMother))then table.insert(ancestorlist,ptrMother:Clone()) end ptrFather:MoveTo(fhGetValueAsLink(ptrFamily),'~.HUSB[1]>') if ptrFather:IsNotNull() and (not CheckDuplicate(dupcheck,ptrFather)) then table.insert(ancestorlist,ptrFather:Clone()) end ptrFather:MoveTo(fhGetValueAsLink(ptrFamily),'~.HUSB[2]>') if ptrFather:IsNotNull() and (not CheckDuplicate(dupcheck,ptrFather)) then table.insert(ancestorlist,ptrFather:Clone()) end ptrFamily:MoveNext('SAME_TAG') end iLoop = iLoop + 1 end return ancestorlist end -- End Function --[[ @function: GetDescendantList @description: Returns a list of descendants @parameters: Item Pointer - Must be at Record Level @returns: table of record Item Pointers @requires: CheckDuplicate ]] function GetDescendantList(ptr) local descendantlist = {} local dupcheck = {} local ptrChild = fhNewItemPtr() local ptrFamily = fhNewItemPtr() local ptrFamilyRec = fhNewItemPtr() local ptrBase = fhNewItemPtr table.insert(descendantlist,ptr:Clone()) CheckDuplicate(dupcheck,ptr) iLoop = 1 while iLoop <= #descendantlist do ptrBase = descendantlist[iLoop] -- Loop Family as Spouse ptrFamily:MoveTo(ptrBase,'~.FAMS') while ptrFamily:IsNotNull() do ptrFamilyRec = fhGetValueAsLink(ptrFamily) -- Loop Children ptrChild:MoveTo(ptrFamilyRec,'~.CHIL') while ptrChild:IsNotNull() do local ptrChildRecord = fhGetValueAsLink(ptrChild) if ptrChildRecord:IsNotNull() and not CheckDuplicate(dupcheck,ptrChildRecord) then table.insert(descendantlist,fhGetValueAsLink(ptrChild)) end ptrChild:MoveNext('SAME_TAG') end ptrFamily:MoveNext('SAME_TAG') end iLoop = iLoop + 1 end return descendantlist end -- End Function
Recursive Method
The local functions call themselves recursively, which avoids the iLoop counter. Effectively, each call is for the next generation. The Record Id as well as the Record pointer is included in the lists.
-
--[[ @Function: NewRelative @Description: Adds Record Id and Pointer to table if it does not already exist @Parameters: Record Pointer, Dictionary of Record Id, Array of Record Id & Pointers @Returns: True if Pointer exists and not already listed, else returns False @Requires: None ]] function NewRelative(ptrRec,dicRel,arrRel) if ptrRec:IsNotNull() then local intRec = fhGetRecordId(ptrRec) if not dicRel[intRec] then dicRel[intRec] = true table.insert(arrRel,{ Id=intRec; Rec=ptrRec:Clone(); }) -- Prevent Stopped Working/Not Responding message for large trees if #arrRel % 1000 == 0 then fhSleep(10,7) end return true end end return false end -- function NewRelative --[[ @Function: GetAncestorList @Description: Gets a list of Ancestors @Parameters: Record Pointer @Returns: Array holding Ancestor Record Id and Pointers @Requires: None ]] function GetAncestorList(ptrRec) local arrRelative = { } -- Array of Individual Record Id & Pointers local dicRelative = { } -- Dictionary of processed Individual Record Id local function getAncestor(ptrRec) -- Get next Ancestor generation if NewRelative(ptrRec,dicRelative,arrRelative) then local ptrFamc = fhGetItemPtr(ptrRec,"~.FAMC") -- Cater for multiple parent families while ptrFamc:IsNotNull() do -- Cater for both sex & same sex parents local ptrFam = fhGetValueAsLink(ptrFamc) getAncestor(fhGetItemPtr(ptrFam,"~.HUSB[1]>")) getAncestor(fhGetItemPtr(ptrFam,"~.WIFE[1]>")) getAncestor(fhGetItemPtr(ptrFam,"~.HUSB[2]>")) getAncestor(fhGetItemPtr(ptrFam,"~.WIFE[2]>")) ptrFamc:MoveNext("SAME_TAG") end end end -- local function getAncestor getAncestor(ptrRec) return arrRelative end -- function GetAncestorList --[[ @Function: GetDescendantList @Description: Gets a list of Descendants @Parameters: Record Pointer @Returns: Array holding Descendant Record Id and Pointers @Requires: None ]] function GetDescendantList(ptrRec) local arrRelative = { } -- Array of Individual Record Id & Pointers local dicRelative = { } -- Dictionary of processed Individual Record Id local function getDescends(ptrRec) -- Get next Descendant generation if NewRelative(ptrRec,dicRelative,arrRelative) then local ptrFams = fhGetItemPtr(ptrRec,"~.FAMS") -- Cater for multiple spouse families while ptrFams:IsNotNull() do -- Cater for both sex & same sex partners local ptrFam = fhGetValueAsLink(ptrFams) getDescends(fhGetItemPtr(ptrFam,"~.HUSB[1]>")) getDescends(fhGetItemPtr(ptrFam,"~.WIFE[1]>")) getDescends(fhGetItemPtr(ptrFam,"~.HUSB[2]>")) getDescends(fhGetItemPtr(ptrFam,"~.WIFE[2]>")) local ptrChil = fhGetItemPtr(ptrFam,"~.CHIL") while ptrChil:IsNotNull() do -- Get all the children getDescends(fhGetValueAsLink(ptrChil)) ptrChil:MoveNext("SAME_TAG") end ptrFams:MoveNext("SAME_TAG") end end end -- local function getDescends getDescends(ptrRec) return arrRelative end -- function GetDescendantList --[[ @Function: GetRelationList @Description: Gets a list of all Relations, i.e. in same Pool @Parameters: Record Pointer @Returns: Array holding Relations Record Id and Pointers @Requires: None ]] function GetRelationList(ptrRec) local arrRelative = { } -- Array of Individual Record Id & Pointers local dicRelative = { } -- Dictionary of processed Individual Record Id local dicFamilies = { } -- Dictionary of processed Family Record Id local function getRelation(ptrRec) -- Get next Relationship generation if NewRelative(ptrRec,dicRelative,arrRelative) then for _, strFamx in ipairs ({ "~.FAMC"; "~.FAMS"; }) do -- Cater for all parent/spouse families local ptrFamx = fhGetItemPtr(ptrRec,strFamx) while ptrFamx:IsNotNull() do local ptrFam = fhGetValueAsLink(ptrFamx) local intFam = fhGetRecordId(ptrFam) if not dicFamilies[intFam] then -- New family Record Id dicFamilies[intFam] = true -- Cater for both sex & same sex partners getRelation(fhGetItemPtr(ptrFam,"~.HUSB[1]>")) getRelation(fhGetItemPtr(ptrFam,"~.WIFE[1]>")) getRelation(fhGetItemPtr(ptrFam,"~.HUSB[2]>")) getRelation(fhGetItemPtr(ptrFam,"~.WIFE[2]>")) local ptrChil = fhGetItemPtr(ptrFam,"~.CHIL") while ptrChil:IsNotNull() do -- Get all children getRelation(fhGetValueAsLink(ptrChil)) ptrChil:MoveNext("SAME_TAG") end end ptrFamx:MoveNext("SAME_TAG") end end end end -- local function getRelation getRelation(ptrRec) return arrRelative end -- function GetRelationList
Usage
-
local ptrRoot = fhCallBuiltInFunction("FileRoot") local tblAncs = GetAncestorList(ptrRoot) local tblDesc = GetDescendantList(ptrRoot) local tblRels = GetRelationList(ptrRoot) -- Only for recursive method
Then for recursive versions, that return more complex tables, they can be sorted and simplified.
-
table.sort(tblRel,function(tblA,tblB) return tblA.Id < tblB.Id end) for intIndi = 1, #tblRels do tblRels[intIndi] = tblRels[intIndi].Rec end