I am looking for a way to search for a name within an array, including situations where the search input (of multiple words: first name, surname) may be reversed.
The array would look like this with a series of names.
const names = ['Alan Hope', 'Greg Day', 'Alan Peters']
The search input could be as follows 'peter Al'
What would the code look like to achieve this. This is what I have so far and I know it is totally wrong.
const studentNames = ['Alan Hope', 'Greg Day', 'Alan Peters']
function search () {
const bankingSheet = ss.getSheetByName('Banking')
const searchInput = 'Hope Al'
const searchWords = searchInput.split(/\s /)
const filtered = studentNames.filter(function(name) {
searchWords.every(function(word) {
return name.toString().toLowerCase().indexOf(word) !== -1
})
})
Logger.log(filtered)
}
From what I gather I need to first split the search input into the constituent words. I then need to filter through the names array. For each name in the array I need to check if all search words appear in some way in the name. I think this may involve the every method.
For each name, if the return value is true that is what I need to return.
Is this thinking correct?
Thank you in advance! This is really hurting my head at the moment!
CodePudding user response:
The idea is to define what's meant by a match. The simplest and most rigid match is simple string equality. A softer match would tolerate case differences. Softer still would be a tolerance for a first name / last name inversion. (shown in the snippet).
Softest of all would be a match that tolerates small differences in the names (a reordering would not register as a small difference via a levenshtein check), unless we compared distances between individual tokens).
const names = ['Alan Hope', 'Greg Day', 'Alan Peters']
function softMatch(nameA, nameB) {
if (nameA === nameB) return true;
const reverse = name => name.split(' ').reverse().join(' ')
const lcA = nameA.toLowerCase();
const lcB = nameB.toLowerCase();
if (lcA === lcB) return true; // case insensitive
if (reverse(lcA) === lcB) return true; // order and case insensitive
return false
}
let matches = names.filter(name => softMatch(name, 'peters Alan'))
console.log(matches)
matches = names.filter(name => softMatch(name, 'No Match'))
console.log(matches)
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
If names are to have more than two subnames, and any ordering is a match, then a match can be implemented as follows...
const names = ['Alan Randolph Hope', 'Greg Herbert Walker Day', 'Alan Jefferson Peters']
function softMatch(nameA, nameB) {
let subnamesA = nameA.split(' ').map(n => n.toLowerCase())
let subnamesB = nameB.split(' ').map(n => n.toLowerCase())
// sort lexically and compare
subnamesA = subnamesA.sort();
subnamesB = subnamesB.sort();
return subnamesA.every(function(element, index) {
return element === subnamesB[index];
});
}
let matches = names.filter(name => softMatch(name, 'peters Alan jefferson'))
console.log(matches)
matches = names.filter(name => softMatch(name, 'No Match'))
console.log(matches)
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>