如何从 Google 工作表中不断增长的字符串列表中找到与字符串最接近的匹配项?
How to find the closest match to a string from ever-growing list of strings in Google sheets?
这个问题有点复杂,所以我创建了这张图片:
Question Scenario
你可以找到the sample spreadsheet here.
为了清楚起见,我用字母替换了实际数据。
详情:
我有 10 个固定条件,每个条件都有特定的权重。
可以通过多种方式组合条件,创建大量可能的组合。通过序列化,我已将其减少到 1000 以下。此列名为“所有条件”。
"可用条件"列显示可用的可能组合列表。此列表将继续增长,直到它与“所有条件”列完全匹配。
挑战是:
如何在考虑权重的情况下,从“可用条件”中找到与“所有条件”中每个值最接近的匹配项?
例如,由于 'A, B' 可用,因此它将匹配。但是对于 'A, B, C',我们需要找到 'A, B, C' 或具有最高可用权重的组合,即 'A, B'.
您可以使用 Apps 脚本 custom function 执行以下操作:
- 获取所有
available
组合、fixed
条件和 All
组合(这可以作为自定义函数参数接收)。将逗号分隔的字符串拆分为具有所有不同条件(A
、B
等)的数组。
- 遍历
All
个组合,对每个组合执行以下操作:
- 启动一个
while
循环,循环遍历组合中的每个条件,并过滤掉不匹配的条件,直到 (1) 它到达数组的末尾并找到完全匹配的条件,或者 ( 2) 没有找到匹配项,有一个候选列表。
- 在可能的候选者中(如果未找到匹配项),找到组合权重最大的候选者。
- Return 所有最佳匹配。
代码片段:
// Copyright 2021 Google LLC.
// SPDX-License-Identifier: Apache-2.0
function GET_CLOSEST_COMBOS(range) {
const sheet = SpreadsheetApp.getActiveSheet();
const available = sheet.getRange("A2:B").getValues().filter(row => row[0] !== "")
.map(row => [commaSeparatedToArray(row[0]), row[1]]);
const fixedConditions = available.filter(av => av[1] !== "");
const conditions = range.flat().map(conditionString => commaSeparatedToArray(conditionString));
const bestMatches = conditions.map(condition => {
let i = 0;
let currentAvailable = [...available];
while (i < condition.length) {
const matchExists = currentAvailable.some(av => av[0][i] == condition[i]);
if (matchExists) {
currentAvailable = currentAvailable.filter(av => av[0][i] == condition[i]);
} else {
currentAvailable = currentAvailable.filter(av => av[0].length - 1 !== i);
}
if (!matchExists || (matchExists && i === condition.length - 1)) {
const candidates = currentAvailable.map(av => {
const weight = av[0].reduce((acc, current, index) => {
return acc + fixedConditions.find(cond => cond[0] == current)[1];
}, 0);
return [av[0], weight];
});
const bestMatch = candidates.reduce((max, candidate) => max[1] >= candidate[1] ? max : candidate);
return bestMatch[0].join(", ");
}
i++;
}
});
return bestMatches;
}
function commaSeparatedToArray(conditionString) {
return conditionString.split(",").map(condition => condition.trim());
}
在脚本中定义后,您可以像使用任何工作表内置函数一样使用它。此函数接受对应于 All combinations
的范围,您要为其检索最佳匹配(在本例中为 D2:D
)。
示例:
这个问题有点复杂,所以我创建了这张图片: Question Scenario
你可以找到the sample spreadsheet here. 为了清楚起见,我用字母替换了实际数据。
详情:
我有 10 个固定条件,每个条件都有特定的权重。
可以通过多种方式组合条件,创建大量可能的组合。通过序列化,我已将其减少到 1000 以下。此列名为“所有条件”。
"可用条件"列显示可用的可能组合列表。此列表将继续增长,直到它与“所有条件”列完全匹配。
挑战是: 如何在考虑权重的情况下,从“可用条件”中找到与“所有条件”中每个值最接近的匹配项?
例如,由于 'A, B' 可用,因此它将匹配。但是对于 'A, B, C',我们需要找到 'A, B, C' 或具有最高可用权重的组合,即 'A, B'.
您可以使用 Apps 脚本 custom function 执行以下操作:
- 获取所有
available
组合、fixed
条件和All
组合(这可以作为自定义函数参数接收)。将逗号分隔的字符串拆分为具有所有不同条件(A
、B
等)的数组。 - 遍历
All
个组合,对每个组合执行以下操作: - 启动一个
while
循环,循环遍历组合中的每个条件,并过滤掉不匹配的条件,直到 (1) 它到达数组的末尾并找到完全匹配的条件,或者 ( 2) 没有找到匹配项,有一个候选列表。 - 在可能的候选者中(如果未找到匹配项),找到组合权重最大的候选者。
- Return 所有最佳匹配。
代码片段:
// Copyright 2021 Google LLC.
// SPDX-License-Identifier: Apache-2.0
function GET_CLOSEST_COMBOS(range) {
const sheet = SpreadsheetApp.getActiveSheet();
const available = sheet.getRange("A2:B").getValues().filter(row => row[0] !== "")
.map(row => [commaSeparatedToArray(row[0]), row[1]]);
const fixedConditions = available.filter(av => av[1] !== "");
const conditions = range.flat().map(conditionString => commaSeparatedToArray(conditionString));
const bestMatches = conditions.map(condition => {
let i = 0;
let currentAvailable = [...available];
while (i < condition.length) {
const matchExists = currentAvailable.some(av => av[0][i] == condition[i]);
if (matchExists) {
currentAvailable = currentAvailable.filter(av => av[0][i] == condition[i]);
} else {
currentAvailable = currentAvailable.filter(av => av[0].length - 1 !== i);
}
if (!matchExists || (matchExists && i === condition.length - 1)) {
const candidates = currentAvailable.map(av => {
const weight = av[0].reduce((acc, current, index) => {
return acc + fixedConditions.find(cond => cond[0] == current)[1];
}, 0);
return [av[0], weight];
});
const bestMatch = candidates.reduce((max, candidate) => max[1] >= candidate[1] ? max : candidate);
return bestMatch[0].join(", ");
}
i++;
}
});
return bestMatches;
}
function commaSeparatedToArray(conditionString) {
return conditionString.split(",").map(condition => condition.trim());
}
在脚本中定义后,您可以像使用任何工作表内置函数一样使用它。此函数接受对应于 All combinations
的范围,您要为其检索最佳匹配(在本例中为 D2:D
)。