在 Google 表格中运行脚本的工作方式与 Excel 不同,后者可能有好有坏。拥有 Excel VBA 背景,您必须转变思维方式。
- 在 Excel 中编写用户定义函数 (UDF) 时,您可以像这样传递
Range 参数:=MyUDF(A1,C3)。函数签名将为您提供两个 Range 对象作为参数。在 Google 表格中,情况并非如此。所有范围参数都转换为值(或值数组)。这意味着,如果您在表格单元格中键入=MyUDF(A1,C3,您的脚本代码将获得 A1 的值和 C3 的值。这不是你想要的。
基于this answer,您最好的选择是解析原始单元格公式并提取范围参数。然后,您可以为您的 UDF 创建一个 Range 对象。这是一套迂回的逻辑,但与 Sheets 实现底层函数和脚本的方式是一致的。以下函数将从活动单元格中提取公式并对其进行解析,从而为您提供预期的范围对象。
注意:如果 UDF 调用位于复合公式中,您必须修改此函数以挑选出您的 UDF 调用。例如,如果您的
公式为=MyUDF(A1,C3)。但是如果你的公式是 不工作
=If(A2='Yes','',MyUDF(A1,C3))。可以修改函数来检测
这个,但这里没有提供。
function GetParamRange(paramNumber) {
var activeSheet = SpreadsheetApp.getActiveSheet();
var formula = SpreadsheetApp.getActiveRange().getFormula();
//--- expecting two parameters, split here and check
const MAXARGS = 2;
var args = formula.match(/=\w+\((.*)\)/i)[1].split(',');
if (args.length != MAXARGS) {
throw new Error('in GetParamRange: ' + args.join(',') + '- invalid number of arguments, expecting ' + MAXARGS);
}
if ((paramNumber < 1) || (paramNumber > MAXARGS)) {
throw new Error('in GetParamRange: invalid parameter requested, must be ' + MAXARGS + ' or less');
}
//--- is the worksheet specified?
var paramRange;
var parts = args[paramNumber-1].split('!');
if (parts.length == 2) {
//--- use worksheet and cell to get the range
Logger.log('activeSheet = ' + activeSheet.getName());
ss = SpreadsheetApp.getActive().getSheetByName(parts[0].replace(/\'/g, ''));
paramRange = ss.getRange(parts[1]);
}
else {
//--- get the range from the active sheet
paramRange = activeSheet.getRange(parts[0]);
}
return(paramRange);
}
- 在我列出实际函数之前,您必须了解编码 UDF 的一个重要方面。在 Excel 中,您可以将 UDF 显式标记为
Volatile。在 Google 表格中,这是不可能的。老实说,我使用 Google 表格的工作还不够多,无法了解所有详细信息,因此您必须四处搜索。
- 用户定义函数本身接受两个参数,但这些参数实际上是通过调用
GetParamRange 创建的。从那里,这是一个简单的循环来添加您的运行总和(带有额外的安全限制)。
如果超过安全限制会抛出错误。
/**
* ParentChildCalc1
* @customfunction ParentChildCalc1
* @param {string} critCell Critical Cell
* @param {number} valCell Value Cell
* @returns {number} ParentChildCalc1
*/
function ParentChildCalc1(critCell, valCell) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var critCell = GetParamRange(1);
var valCell = GetParamRange(2);
const safetyNet = 5000;
var runningSum = 0;
var numYes = 0;
for (var i = 0; !valCell.offset(i,0).isBlank(); i++) {
if (critCell.offset(i,0).getValue() == 'Yes') {
numYes++;
if (numYes == 2) break;
if (i == safetyNet) {
throw new Error('exceeded safety net limit in ParentChildCalc1, searching ' + safetyNet + ' rows.');
}
}
runningSum = runningSum + valCell.offset(i,0).getValue();
}
return (runningSum);
}
这是单个块中的整个代码:
/**
* ParentChildCalc1
* @customfunction ParentChildCalc1
* @param {string} critCell Critical Cell
* @param {number} valCell Value Cell
* @returns {number} ParentChildCalc1
*/
function ParentChildCalc1(critCell, valCell) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var critCell = GetParamRange(1);
var valCell = GetParamRange(2);
const safetyNet = 5000;
var runningSum = 0;
var numYes = 0;
for (var i = 0; !valCell.offset(i,0).isBlank(); i++) {
if (critCell.offset(i,0).getValue() == 'Yes') {
numYes++;
if (numYes == 2) break;
if (i == safetyNet) {
throw new Error('exceeded safety net limit in ParentChildCalc1, searching ' + safetyNet + ' rows.');
}
}
runningSum = runningSum + valCell.offset(i,0).getValue();
}
return (runningSum);
}
function GetParamRange(paramNumber) {
var activeSheet = SpreadsheetApp.getActiveSheet();
var formula = SpreadsheetApp.getActiveRange().getFormula();
//--- expecting two parameters, split here and check
const MAXARGS = 2;
var args = formula.match(/=\w+\((.*)\)/i)[1].split(',');
if (args.length != MAXARGS) {
throw new Error('in GetParamRange: ' + args.join(',') + '- invalid number of arguments, expecting ' + MAXARGS);
}
if ((paramNumber < 1) || (paramNumber > MAXARGS)) {
throw new Error('in GetParamRange: invalid parameter requested, must be ' + MAXARGS + ' or less');
}
//--- is the worksheet specified?
var paramRange;
var parts = args[paramNumber-1].split('!');
if (parts.length == 2) {
//--- use worksheet and cell to get the range
Logger.log('activeSheet = ' + activeSheet.getName());
ss = SpreadsheetApp.getActive().getSheetByName(parts[0].replace(/\'/g, ''));
paramRange = ss.getRange(parts[1]);
}
else {
//--- get the range from the active sheet
paramRange = activeSheet.getRange(parts[0]);
}
return(paramRange);
}
因此,要使用此功能,只需将公式 =ParentChildCalc1(D7,C7) 放入单元格 E7 中即可。