Google 张中的模糊匹配
Fuzzy matching in Google Sheets
尝试将 GoogleSheets 中的两列与 C 列中的此公式进行比较:
=if(A1=B1,"","Mismatch")
工作正常,但我收到很多误报:
A.
B
C
MARY JO
Mary Jo
JAY, TIM
TIM JAY
Mismatch
Sam Ron
Sam Ron
Mismatch
Jack *Ma
Jack MA
Mismatch
有什么想法吗?
通过 Google Sheets 公式实现模糊匹配会很困难。如果您想一次填充所有行,我建议为此使用自定义公式或完整脚本(均通过 Google Apps 脚本)。
自定义公式:
function fuzzyMatch(string1, string2) {
string1 = string1.toLowerCase()
string2 = string2.toLowerCase();
var n = -1;
for(i = 0; char = string2[i]; i++)
if (!~(n = string1.indexOf(char, n + 1)))
return 'Mismatch';
};
它的作用是比较第二个字符串的字符顺序是否与第一个字符串的字符顺序相同。请参阅下面的示例数据,了解 return 不匹配的情况。
输出:
注:
- 最后一行不匹配,因为第二个字符串中有
r
而在第一个字符串中找不到,因此不符合正确的顺序。
- 如果这不符合您的测试用例,请添加一个更明确的列表,该列表将显示 formula/function 的预期输出,以便对其进行调整,或者查看仅使用 [=39= 的 player0 的答案] 表格公式和条件不太严格。
参考:
尝试:
=ARRAYFORMULA(IFERROR(IF(LEN(
REGEXREPLACE(REGEXREPLACE(LOWER(A1:A), "[^a-z ]", ),
LOWER("["&B1:B&"]"), ))>0, "mismatch", )))
这使用基于分数的方法来确定匹配。您可以根据该分数确定哪些 is/isn 不匹配:
Score Formula = getMatchScore(A1,B1)
Match Formula = if(C1<.7,"mismatch",)
function getMatchScore(strA, strB, ignoreCase=true) {
strA = String(strA);
strB = String(strB)
const toLowerCase = ignoreCase ? str => str.toLowerCase() : str => str;
const splitWords = str => str.split(/\b/);
let [maxLenStr, minLenStr] = strA.length > strB.length ? [strA, strB] : [strB, strA];
maxLenStr = toLowerCase(maxLenStr);
minLenStr = toLowerCase(minLenStr);
const maxLength = maxLenStr.length;
const minLength = minLenStr.length;
const lenScore = minLength / maxLength;
const orderScore = Array.from(maxLenStr).reduce(
(oldItem, nItem, index) => nItem === minLenStr[index] ? oldItem + 1 : oldItem, 0
) / maxLength;
const maxKeyWords = splitWords(maxLenStr);
const minKeyWords = splitWords(minLenStr);
const keywordScore = minKeyWords.reduce(({ score, searchWord }, nItem) => {
const newSearchWord = searchWord?.replace(new RegExp(nItem, ignoreCase ? 'i' : ''), '');
score += searchWord.length != newSearchWord.length ? 1: 0;
return { score, searchWord: newSearchWord };
}, { score: 0, searchWord: maxLenStr }).score / minKeyWords.length;
const sortedMaxLenStr = Array.from(maxKeyWords.sort().join(''));
const sortedMinLenStr = Array.from(minKeyWords.sort().join(''));
const charScore = sortedMaxLenStr.reduce((oldItem, nItem, index) => {
const surroundingChars = [sortedMinLenStr[index-1], sortedMinLenStr[index], sortedMinLenStr[index+1]]
.filter(char => char != undefined);
return surroundingChars.includes(nItem)? oldItem + 1 : oldItem
}, 0) / maxLength;
const score = (lenScore * .15) + (orderScore * .25) + (charScore * .25) + (keywordScore * .35);
return score;
}
尝试将 GoogleSheets 中的两列与 C 列中的此公式进行比较:
=if(A1=B1,"","Mismatch")
工作正常,但我收到很多误报:
A. | B | C |
---|---|---|
MARY JO | Mary Jo | |
JAY, TIM | TIM JAY | Mismatch |
Sam Ron | Sam Ron | Mismatch |
Jack *Ma | Jack MA | Mismatch |
有什么想法吗?
通过 Google Sheets 公式实现模糊匹配会很困难。如果您想一次填充所有行,我建议为此使用自定义公式或完整脚本(均通过 Google Apps 脚本)。
自定义公式:
function fuzzyMatch(string1, string2) {
string1 = string1.toLowerCase()
string2 = string2.toLowerCase();
var n = -1;
for(i = 0; char = string2[i]; i++)
if (!~(n = string1.indexOf(char, n + 1)))
return 'Mismatch';
};
它的作用是比较第二个字符串的字符顺序是否与第一个字符串的字符顺序相同。请参阅下面的示例数据,了解 return 不匹配的情况。
输出:
注:
- 最后一行不匹配,因为第二个字符串中有
r
而在第一个字符串中找不到,因此不符合正确的顺序。 - 如果这不符合您的测试用例,请添加一个更明确的列表,该列表将显示 formula/function 的预期输出,以便对其进行调整,或者查看仅使用 [=39= 的 player0 的答案] 表格公式和条件不太严格。
参考:
尝试:
=ARRAYFORMULA(IFERROR(IF(LEN(
REGEXREPLACE(REGEXREPLACE(LOWER(A1:A), "[^a-z ]", ),
LOWER("["&B1:B&"]"), ))>0, "mismatch", )))
这使用基于分数的方法来确定匹配。您可以根据该分数确定哪些 is/isn 不匹配:
Score Formula = getMatchScore(A1,B1)
Match Formula = if(C1<.7,"mismatch",)
function getMatchScore(strA, strB, ignoreCase=true) {
strA = String(strA);
strB = String(strB)
const toLowerCase = ignoreCase ? str => str.toLowerCase() : str => str;
const splitWords = str => str.split(/\b/);
let [maxLenStr, minLenStr] = strA.length > strB.length ? [strA, strB] : [strB, strA];
maxLenStr = toLowerCase(maxLenStr);
minLenStr = toLowerCase(minLenStr);
const maxLength = maxLenStr.length;
const minLength = minLenStr.length;
const lenScore = minLength / maxLength;
const orderScore = Array.from(maxLenStr).reduce(
(oldItem, nItem, index) => nItem === minLenStr[index] ? oldItem + 1 : oldItem, 0
) / maxLength;
const maxKeyWords = splitWords(maxLenStr);
const minKeyWords = splitWords(minLenStr);
const keywordScore = minKeyWords.reduce(({ score, searchWord }, nItem) => {
const newSearchWord = searchWord?.replace(new RegExp(nItem, ignoreCase ? 'i' : ''), '');
score += searchWord.length != newSearchWord.length ? 1: 0;
return { score, searchWord: newSearchWord };
}, { score: 0, searchWord: maxLenStr }).score / minKeyWords.length;
const sortedMaxLenStr = Array.from(maxKeyWords.sort().join(''));
const sortedMinLenStr = Array.from(minKeyWords.sort().join(''));
const charScore = sortedMaxLenStr.reduce((oldItem, nItem, index) => {
const surroundingChars = [sortedMinLenStr[index-1], sortedMinLenStr[index], sortedMinLenStr[index+1]]
.filter(char => char != undefined);
return surroundingChars.includes(nItem)? oldItem + 1 : oldItem
}, 0) / maxLength;
const score = (lenScore * .15) + (orderScore * .25) + (charScore * .25) + (keywordScore * .35);
return score;
}