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!
You may also try this sample implementation below to find the matched name even when the search input is reversed or surname/first name is abbreviated (e.g. Alan H. or Al Hope) for a more flexible search method:
const studentNames = ['Alan Randolph Hope', 'Greg Herbert Walker Day', 'Alan Jefferson Peters']
function findMatch(){
var match = run("Hope Al");
console.log(match);
console.log(match.length>0);
}
function run(searchString) {
var res = [];
let findDuplicates = arr => arr.filter((item, index) => arr.indexOf(item) != index);
var searches = searchString.split(" ");
searches.forEach(search=>{
studentNames.forEach(name =>{
if(name.toLowerCase().includes(search.toLowerCase())){
res.push(name);
}
});
});
return [...new Set(findDuplicates(res))];
}
Hope Al:Greg D:Reference:
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)
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)