Search All Possible Names.fh_lua--[[
@title: Search All Possible Names
@author: Jane Taubman
@lastupdated: November 2020
@version: 1.6
@Licence: This plugin is copyright (c) 2020 Jane Taubman and contributors, and is licensed under the MIT License
which is hereby incorporated by reference (see https://pluginstore.family-historian.co.uk/fh-plugin-licence)
@description:
Script reads through all individuals and lists all variations of Names from the Name field
and for married women lists them under their married names as well, where the
given name and surname fields contain the entered search fields.
Please Note: All family as Spouse occurrences will be searched even if there is no marriage recorded.
@changes:
1.2 If user cancels the prompt it now simply returns
1.2 Corrected incorrect operation where user presses enter rather than pressing the OK button.
1.3 Added option to search using basic soundex for both the name and surname.
1.4 Added Default values from the currently selected record if there is one
1.5 Fix identification of Secondary Names and a search of subfields for Nickname, Used Name and the Surname and Given Name subfields.
1.6 V7 Compatibility
]]
require("iuplua");
iup.SetGlobal("CUSTOMQUITMESSAGE","YES");
function trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
-- Convert String to Soundex
function soundex(str)
local codes = {
A=0,E=0,I=0,O=0,U=0,Y=0,H=0,
B=1,P=1,F=1,V=1,
C=2,S=2,G=2,J=2,K=2,Q=2,X=2,Z=2,
D=3,T=3,
L=4,
M=5,N=5,
R=6
}
local strSoundex = ''
local strCap = string.upper(str)
local strSoundex = string.sub(strCap,1,1)
local strRest = string.sub(strCap,2)
for letter in string.gmatch(strRest, "%a") do
local l = string.len(strSoundex)
local strLast = string.sub(strSoundex,l,l)
if codes[letter] ~= nil then
if codes[letter] ~= 0 and codes[letter] ~= tonumber(strLast) then
strSoundex = strSoundex..codes[letter]
end
end
end
strSoundex = strSoundex..'0000'
return string.sub(strSoundex,1,4)
end
-- Convert all Words To Soundex
function soundexall(str)
local strSoundex = ''
for word in string.gmatch(str, "%a+") do
strSoundex = strSoundex..' '..soundex(word)
end
return strSoundex
end
function comparestring(string1,string2,bUseSoundex)
local str1 = trim(string1:lower())
local str2 = trim(string2:lower())
if bUseSoundex then
str1 = soundexall(str1)
str2 = soundexall(str2)
end
local ipos
if string2 == nil then
return true
else
ipos = string.find(str1,str2)
return not(ipos == nil)
end
end
function chkRow(sSurname,sGiven,ptrIndi,ptrFam,type)
if comparestring(sSurname,strSurname,bUseSoundex) and comparestring(sGiven,strFirstName,bUseSoundex) then
count = count + 1
tblSurname[count] = sSurname:upper()
tblGivenAll[count] = sGiven
tblSoundex[count] = soundexall(tblSurname[count])..' '..soundexall(tblGivenAll[count])
tblRecord[count] = ptrIndividual:Clone()
tblRecordNo[count] = fhGetRecordId(ptrIndividual)
if type == 3 then
tblFamilyRecord[count] = ptrFam:Clone()
tblMarriageDate[count] = fhGetItemPtr(ptrFam,'FAM.MARR.DATE')
end
tblLifeDates[count] = fhCallBuiltInFunction('Lifedates',ptrIndividual)
tblNameType[count] = tblTypes[type]
end
end
-- Load IUP for Prompt Window.
require( "iuplua" )
-- If there is a current individual record grab the details as default for the search
ptrList = fhGetCurrentRecordSel('INDI')
if #ptrList > 0 then
surname = string.lower(fhGetItemText(ptrList[1],'~.NAME:SURNAME'))
forenames = string.lower(fhGetItemText(ptrList[1],'~.NAME:GIVEN_ALL'))
else
surname = ''
forenames = ''
end
-- Prompt User to Confirm Options
bOK,strFirstName,strSurname,bSoundex =
iup.GetParam("Search All Possible Names", param_action,
"Given Name Contains: %s\n"..
"Surname Contains: %s\n"..
"Use Soundex for search: %b\n"
,
forenames,surname,0)
if not(bOK) then
return
end
if bSoundex == 1 then
bUseSoundex = true
else
bUseSoundex = false
end
ptrIndividual = fhNewItemPtr() -- declare pointer
ptrIndividual:MoveToFirstRecord('INDI') -- set the first to point to the first Note record
ptrName = fhNewItemPtr()
ptrFamS = fhNewItemPtr()
ptrFam = fhNewItemPtr()
count = 0
-- Define Columns
tblSurname = {}
tblGivenAll = {}
tblSoundex = {}
tblRecord = {}
tblRecordNo = {}
tblFamilyRecord = {}
tblLifeDates = {}
tblMarriageDate = {}
tblNameType = {}
tblTypes = {'Primary','Secondary','Married','Subfield'}
while not ptrIndividual:IsNull() do
-- Get All Names from Name Fields
ptrName:MoveTo(ptrIndividual,"~.NAME") -- Get the Name for the INDI Record.
local index = 1
local type = 1
while not ptrName:IsNull() do
chkRow(fhGetItemText(ptrName,'~:SURNAME'),fhGetItemText(ptrName,'~:GIVEN_ALL'),ptrIndividual,nil,type)
-- For Each Name Look for subfields
local sSurname = fhGetItemText(ptrName,'~:SURNAME')
local sGivenAll = fhGetItemText(ptrName,'~:GIVEN_ALL')
-- INDI.NAME[1].GIVN
local sGiven = fhGetItemText(ptrName,'~.GIVN')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
-- INDI.NAME[1].NICK
local sGiven = fhGetItemText(ptrName,'~.NICK')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
-- INDI.NAME[1]._USED
local sGiven = fhGetItemText(ptrName,'~._USED')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
-- INDI.NAME[1].SURN
local sSurname = fhGetItemText(ptrName,'~.SURN')
if sSurname ~= '' and sSurname ~= fhGetItemText(ptrName,'~:SURNAME') then
chkRow(sSurname,sGivenAll,ptrIndividual,nil,4)
end
-- For Females check each set of Forenames combined with the Husbands surname.
strSex = fhGetItemText(ptrIndividual,'INDI.SEX')
if (strSex == 'Female') then
ptrFamS:MoveTo(ptrIndividual,"~.FAMS")
while not ptrFamS:IsNull() do
ptrFam = fhGetValueAsLink(ptrFamS)
famStat = fhGetItemText(ptrFam,'~._STAT')
if famStat ~= 'Never Married' and famStat ~= 'Unmarried Couple' then
local sSurname = fhGetItemText(ptrFam,'~.HUSB>NAME:SURNAME')
local sGiven = fhGetItemText(ptrName,'~:GIVEN_ALL')
local sGivenAll = sGiven
chkRow(sSurname,sGiven,ptrIndividual,ptrFam,3)
-- INDI.NAME[1].GIVN
local sGiven = fhGetItemText(ptrName,'~.GIVN')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
-- INDI.NAME[1].NICK
local sGiven = fhGetItemText(ptrName,'~.NICK')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
-- INDI.NAME[1]._USED
local sGiven = fhGetItemText(ptrName,'~._USED')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
end
ptrFamS:MoveNext("SAME_TAG") -- next Family as spouse
end
end
index = index + 1
type = 2
ptrName:MoveNext("SAME_TAG") -- next Name
end
ptrIndividual:MoveNext() -- next individual record
end
if (#tblSurname > 0) then
strTitle = "Matches for "..strFirstName..' '..strSurname
strPrintTitle = "Search All Possible Names"
fhOutputResultSetTitles(strTitle, strPrintTitle, strTitle.." (%#x)")
fhOutputResultSetColumn("Surname", "text", tblSurname, #tblSurname, 80, "align_left", 1, true)
fhOutputResultSetColumn("Given Names", "text", tblGivenAll, #tblSurname, 80, "align_left", 2, true)
fhOutputResultSetColumn("Type", "text", tblNameType, #tblSurname, 40, "align_left", 0, true)
fhOutputResultSetColumn("Lifedates", "text", tblLifeDates, #tblSurname, 40, "align_left", 3, true)
fhOutputResultSetColumn("Marriage", "item", tblMarriageDate, #tblSurname, 80, "align_left", 0, true)
fhOutputResultSetColumn("Record", "item", tblRecord, #tblSurname, 140, "align_left")
fhOutputResultSetColumn("Ref", "integer", tblRecordNo, #tblSurname, 20, "align_right")
fhOutputResultSetColumn("Family", "item", tblFamilyRecord, #tblSurname, 140, "align_left")
fhOutputResultSetColumn("Soundex", "text", tblSoundex, #tblSurname, 140, "align_left")
else
fhMessageBox('No matches found for '..strFirstName..' '..strSurname)
end
--[[
@title: Search All Possible Names
@author: Jane Taubman
@lastupdated: November 2020
@version: 1.6
@Licence: This plugin is copyright (c) 2020 Jane Taubman and contributors, and is licensed under the MIT License
which is hereby incorporated by reference (see https://pluginstore.family-historian.co.uk/fh-plugin-licence)
@description:
Script reads through all individuals and lists all variations of Names from the Name field
and for married women lists them under their married names as well, where the
given name and surname fields contain the entered search fields.
Please Note: All family as Spouse occurrences will be searched even if there is no marriage recorded.
@changes:
1.2 If user cancels the prompt it now simply returns
1.2 Corrected incorrect operation where user presses enter rather than pressing the OK button.
1.3 Added option to search using basic soundex for both the name and surname.
1.4 Added Default values from the currently selected record if there is one
1.5 Fix identification of Secondary Names and a search of subfields for Nickname, Used Name and the Surname and Given Name subfields.
1.6 V7 Compatibility
]]
require("iuplua");
iup.SetGlobal("CUSTOMQUITMESSAGE","YES");
function trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
-- Convert String to Soundex
function soundex(str)
local codes = {
A=0,E=0,I=0,O=0,U=0,Y=0,H=0,
B=1,P=1,F=1,V=1,
C=2,S=2,G=2,J=2,K=2,Q=2,X=2,Z=2,
D=3,T=3,
L=4,
M=5,N=5,
R=6
}
local strSoundex = ''
local strCap = string.upper(str)
local strSoundex = string.sub(strCap,1,1)
local strRest = string.sub(strCap,2)
for letter in string.gmatch(strRest, "%a") do
local l = string.len(strSoundex)
local strLast = string.sub(strSoundex,l,l)
if codes[letter] ~= nil then
if codes[letter] ~= 0 and codes[letter] ~= tonumber(strLast) then
strSoundex = strSoundex..codes[letter]
end
end
end
strSoundex = strSoundex..'0000'
return string.sub(strSoundex,1,4)
end
-- Convert all Words To Soundex
function soundexall(str)
local strSoundex = ''
for word in string.gmatch(str, "%a+") do
strSoundex = strSoundex..' '..soundex(word)
end
return strSoundex
end
function comparestring(string1,string2,bUseSoundex)
local str1 = trim(string1:lower())
local str2 = trim(string2:lower())
if bUseSoundex then
str1 = soundexall(str1)
str2 = soundexall(str2)
end
local ipos
if string2 == nil then
return true
else
ipos = string.find(str1,str2)
return not(ipos == nil)
end
end
function chkRow(sSurname,sGiven,ptrIndi,ptrFam,type)
if comparestring(sSurname,strSurname,bUseSoundex) and comparestring(sGiven,strFirstName,bUseSoundex) then
count = count + 1
tblSurname[count] = sSurname:upper()
tblGivenAll[count] = sGiven
tblSoundex[count] = soundexall(tblSurname[count])..' '..soundexall(tblGivenAll[count])
tblRecord[count] = ptrIndividual:Clone()
tblRecordNo[count] = fhGetRecordId(ptrIndividual)
if type == 3 then
tblFamilyRecord[count] = ptrFam:Clone()
tblMarriageDate[count] = fhGetItemPtr(ptrFam,'FAM.MARR.DATE')
end
tblLifeDates[count] = fhCallBuiltInFunction('Lifedates',ptrIndividual)
tblNameType[count] = tblTypes[type]
end
end
-- Load IUP for Prompt Window.
require( "iuplua" )
-- If there is a current individual record grab the details as default for the search
ptrList = fhGetCurrentRecordSel('INDI')
if #ptrList > 0 then
surname = string.lower(fhGetItemText(ptrList[1],'~.NAME:SURNAME'))
forenames = string.lower(fhGetItemText(ptrList[1],'~.NAME:GIVEN_ALL'))
else
surname = ''
forenames = ''
end
-- Prompt User to Confirm Options
bOK,strFirstName,strSurname,bSoundex =
iup.GetParam("Search All Possible Names", param_action,
"Given Name Contains: %s\n"..
"Surname Contains: %s\n"..
"Use Soundex for search: %b\n"
,
forenames,surname,0)
if not(bOK) then
return
end
if bSoundex == 1 then
bUseSoundex = true
else
bUseSoundex = false
end
ptrIndividual = fhNewItemPtr() -- declare pointer
ptrIndividual:MoveToFirstRecord('INDI') -- set the first to point to the first Note record
ptrName = fhNewItemPtr()
ptrFamS = fhNewItemPtr()
ptrFam = fhNewItemPtr()
count = 0
-- Define Columns
tblSurname = {}
tblGivenAll = {}
tblSoundex = {}
tblRecord = {}
tblRecordNo = {}
tblFamilyRecord = {}
tblLifeDates = {}
tblMarriageDate = {}
tblNameType = {}
tblTypes = {'Primary','Secondary','Married','Subfield'}
while not ptrIndividual:IsNull() do
-- Get All Names from Name Fields
ptrName:MoveTo(ptrIndividual,"~.NAME") -- Get the Name for the INDI Record.
local index = 1
local type = 1
while not ptrName:IsNull() do
chkRow(fhGetItemText(ptrName,'~:SURNAME'),fhGetItemText(ptrName,'~:GIVEN_ALL'),ptrIndividual,nil,type)
-- For Each Name Look for subfields
local sSurname = fhGetItemText(ptrName,'~:SURNAME')
local sGivenAll = fhGetItemText(ptrName,'~:GIVEN_ALL')
-- INDI.NAME[1].GIVN
local sGiven = fhGetItemText(ptrName,'~.GIVN')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
-- INDI.NAME[1].NICK
local sGiven = fhGetItemText(ptrName,'~.NICK')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
-- INDI.NAME[1]._USED
local sGiven = fhGetItemText(ptrName,'~._USED')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
-- INDI.NAME[1].SURN
local sSurname = fhGetItemText(ptrName,'~.SURN')
if sSurname ~= '' and sSurname ~= fhGetItemText(ptrName,'~:SURNAME') then
chkRow(sSurname,sGivenAll,ptrIndividual,nil,4)
end
-- For Females check each set of Forenames combined with the Husbands surname.
strSex = fhGetItemText(ptrIndividual,'INDI.SEX')
if (strSex == 'Female') then
ptrFamS:MoveTo(ptrIndividual,"~.FAMS")
while not ptrFamS:IsNull() do
ptrFam = fhGetValueAsLink(ptrFamS)
famStat = fhGetItemText(ptrFam,'~._STAT')
if famStat ~= 'Never Married' and famStat ~= 'Unmarried Couple' then
local sSurname = fhGetItemText(ptrFam,'~.HUSB>NAME:SURNAME')
local sGiven = fhGetItemText(ptrName,'~:GIVEN_ALL')
local sGivenAll = sGiven
chkRow(sSurname,sGiven,ptrIndividual,ptrFam,3)
-- INDI.NAME[1].GIVN
local sGiven = fhGetItemText(ptrName,'~.GIVN')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
-- INDI.NAME[1].NICK
local sGiven = fhGetItemText(ptrName,'~.NICK')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
-- INDI.NAME[1]._USED
local sGiven = fhGetItemText(ptrName,'~._USED')
if sGiven ~= "" and sGiven ~= sGivenAll then
chkRow(sSurname,sGiven,ptrIndividual,nil,4)
end
end
ptrFamS:MoveNext("SAME_TAG") -- next Family as spouse
end
end
index = index + 1
type = 2
ptrName:MoveNext("SAME_TAG") -- next Name
end
ptrIndividual:MoveNext() -- next individual record
end
if (#tblSurname > 0) then
strTitle = "Matches for "..strFirstName..' '..strSurname
strPrintTitle = "Search All Possible Names"
fhOutputResultSetTitles(strTitle, strPrintTitle, strTitle.." (%#x)")
fhOutputResultSetColumn("Surname", "text", tblSurname, #tblSurname, 80, "align_left", 1, true)
fhOutputResultSetColumn("Given Names", "text", tblGivenAll, #tblSurname, 80, "align_left", 2, true)
fhOutputResultSetColumn("Type", "text", tblNameType, #tblSurname, 40, "align_left", 0, true)
fhOutputResultSetColumn("Lifedates", "text", tblLifeDates, #tblSurname, 40, "align_left", 3, true)
fhOutputResultSetColumn("Marriage", "item", tblMarriageDate, #tblSurname, 80, "align_left", 0, true)
fhOutputResultSetColumn("Record", "item", tblRecord, #tblSurname, 140, "align_left")
fhOutputResultSetColumn("Ref", "integer", tblRecordNo, #tblSurname, 20, "align_right")
fhOutputResultSetColumn("Family", "item", tblFamilyRecord, #tblSurname, 140, "align_left")
fhOutputResultSetColumn("Soundex", "text", tblSoundex, #tblSurname, 140, "align_left")
else
fhMessageBox('No matches found for '..strFirstName..' '..strSurname)
endSource:Search-All-Possible-Names.fh_lua