如何将范围传递给Google Spreadsheets中的自定义函数?


44

我想创建一个函数,它需要一个范围...类似:

function myFunction(range) {
  var firstColumn = range.getColumn();
  // loop over the range
}

单元格将使用以下方式引用它:

=myFunction(A1:A4)

问题是,当我尝试执行此操作时,参数似乎只是将值传递给函数。因此,我不能使用任何Range方法,例如getColumn()。当我尝试这样做时,会出现以下错误:

错误:TypeError:在对象1,2,3中找不到函数getColumn。

如何将实际范围而不只是值发送到我的自定义函数之一?

Answers:


22

因此,我一直在努力寻找一个好的答案,这是我发现的:

  1. 未经修改的范围参数会传入范围内的单元格,而不是范围本身(如Gergely所述),例如:执行此操作时=myFunction(a1:a2)

  2. 要在函数中使用范围,您需要先将范围作为字符串传递(例如:)=myFunction("a1:a2"),然后使用函数内的以下代码将其转换为范围:

    Function myFunction(pRange){
      var sheet = SpreadsheetApp.getActiveSpreadsheet();
      var range = sheet.getRange(pRange);
    }
    
  3. 最后,如果您希望传递一个范围的好处(例如将范围引用智能复制到其他单元格)并且想要以字符串形式传递它,则必须使用一个默认函数,该函数接受一个范围作为参数并输出一个您可以使用的字符串。我在下面详细介绍了两种方法,但我更喜欢第二种方法。

    对于所有Google电子表格: =myFunction(ADDRESS(row(A1),COLUMN(A1),4)&":"&ADDRESS(row(A2),COLUMN(A2),4));

    对于新的Google表格: =myFunction(CELL("address",A1)&":"&CELL("address",A2));


您能否详细说明“将范围引用智能复制到其他单元格”是什么意思?
艾默生·法鲁吉亚

救生员。可以,谢谢。
Jithin Pavithran '18

@EmersonFarrugia我指的是当您将函数从一个单元格复制到另一个单元格时,Google工作表自动更新相对单元格引用的功能
Nathan Hanna

9

range被视为javascript的2d数组。您可以使用获取行range.length数和使用列数range[0].length。如果要从行r和列中获取值,请c使用:range[r][c]


我正在尝试找出范围内的第一列。例如,在范围C1:F1,我想知道,在列左右的范围开始
Senseful

@Senseful如果您将在类似:的单元格中使用函数=myFunction(C1:F1),则函数内部range[0][0]将返回的值C1range[0][1]将返回的值D1range[0][2]将返回的值E1,等等。
Lipis 2010年

我想我没有清楚地说明自己...看看您对这个问题的回答。您建议我使用=weightedAverage(B3:D3,$B$2:$D$2),我想通过不必发送第二个参数(即,我想将其称为=weightedAverage(B3:D3))来使函数更具错误验证能力,然后使代码自动知道范围从B开始并在B结束D,因此它从第二行获取相应的值。注意:值“ 2”可以硬编码,但“ B”不应硬编码。
有意义的2010年

1
@Senseful一般来说,我反对硬编码的东西,并且我认为加权平均值是否需要两个参数更清楚。因此,如果您要进行硬编码,那么还有很多其他事情可以做。我现在找不到如何摆脱B困境range。我建议您仔细阅读入门指南(goo.gl/hm0xT),以了解如何使用范围和单元格。
Lipis 2010年

6

将a传递Range给Google电子表格函数时,该框架会paramRange.getValues()隐式执行,并且您的函数会以字符串,数字或对象(如Date)的二维数组形式接收Range中的值。该Range对象不会传递到您的自定义电子表格功能。

TYPEOF()下面的函数将告诉您作为参数接收的数据类型。公式

=TYPEOF(A1:A4)

将这样调用脚本:

函数calculateCell(){
  var resultCell = SpreadsheetApp.getActiveSheet()。getActiveCell();
  var paramRange = SpreadsheetApp.getActiveSheet()。getRange('A1:A4');
  var paramValues = paramRange.getValues();

  var resultValue = TYPEOF(paramValues);

  resultCell.setValue(resultValue);
}
函数TYPEOF(value){
  如果(typeof值!=='对象')
    返回typeof值;

  var details ='';
  if(value instanceof Array){
    details + ='['+ value.length +']';
    如果(值[0]数组的实例)
      details + ='['+ value [0] .length +']';
  }

  var className = value.constructor.name;
  返回className +详细信息;
}

3

或者,受@Lipis 注释的启发,您可以将行/列坐标作为附加参数来调用函数:

=myFunction(B1:C2; ROW(B1); COLUMN(B1); ROW(C2); COLUMN(C2))

通过这种形式,可以轻松地将公式复制(或拖动)到其他单元格中,并且将自动调整单元格/范围引用。

您的脚本功能将需要更新为:

function myFunction(rangeValues, startRow, startColumn, endRow, endColumn) {
  var range = SpreadsheetApp.getActiveSheet().getRange(startRow, startColumn, endRow - startRow + 1, endColumn - startColumn + 1);
  return "Range: " + range.getA1Notation();
}

需要注意的是该getRange功能需要startRowstartColumn以及随后行,以及列-没有结束行和结束列。因此,算术。


那是我期望的确切答案。行和列。
Jianwu Chen

3

可以做到的。可以通过在活动单元格(包含公式的单元格)中解析公式来获得对传递范围的引用。这假定自定义函数是单独使用的,而不是作为更复杂的表达式的一部分使用的:例如=myfunction(A1:C3),not =sqrt(4+myfunction(A1:C3))

此函数返回传递范围的第一列索引。

function myfunction(reference) {
  var sheet = SpreadsheetApp.getActiveSheet();
  var formula = SpreadsheetApp.getActiveRange().getFormula();
  var args = formula.match(/=\w+\((.*)\)/i)[1].split('!');
  try {
    if (args.length == 1) {
      var range = sheet.getRange(args[0]);
    }
    else {
      sheet = ss.getSheetByName(args[0].replace(/'/g, ''));
      range = sheet.getRange(args[1]);
    }
  }
  catch(e) {
    throw new Error(args.join('!') + ' is not a valid range');
  }

  // everything so far was only range extraction
  // the specific logic of the function begins here

  var firstColumn = range.getColumn();  // or whatever you want to do with the range
  return firstColumn;
}

我认为,这个帖子还回答以下问题堆栈溢出通过单元格引用的电子表格功能

2

我在user79865的答案中扩展了出色的想法,以使其适用于更多情况,并将多个参数传递给自定义函数。

要使用它,请复制下面的代码,然后在您这样的自定义函数调用中GetParamRanges(),将您的函数名称传递给它:

function CustomFunc(ref1, ref2) {

  var ranges = GetParamRanges("CustomFunc"); // substitute your function name here

  // ranges[0] contains the range object for ref1 (or null)
  // ranges[1] contains the range object for ref2 (or null)

  ... do what you want

  return what_you_want;
}

这是返回单元格颜色的示例:

/**
* Returns the background color of a cell
*
* @param {cell_ref} The address of the cell
* @return The color of the cell
* @customfunction
*/
function GetColor(ref) {

  return GetParamRanges("GetColor")[0].getBackground();
}

这是代码和更多说明:

/**
* Returns an array of the range object(s) referenced by the parameters in a call to a custom function from a cell
* The array will have an entry for each parameter. If the parameter was a reference to a cell or range then 
* its array element will contain the corresponding range object, otherwise it will be null.
*
* Limitations:
* - A range is returned only if a parameter expression is a single reference.
*   For example,=CustomFunc(A1+A2) would not return a range.
* - The parameter expressions in the cell formula may not contain commas or brackets.
*   For example, =CustomFunc(A1:A3,ATAN2(4,3),B:E) would not parse correctly.
* - The custom function may not appear more than once in the cell formula.
* - Sheet names may not contain commas, quotes or closing brackets.
* - The cell formula may contain white space around the commas separating the custom function parameters, or after
*   the custom function name, but not elsewhere within the custom function invocation.
* - There may be other limitations.
* 
* Examples:
*   Cell formula: =CUSTOMFUNC($A$1)
*   Usage:        var ranges = GetParamRanges("CustomFunc");
*   Result:       ranges[0]: range object for cell A1 in the sheet containing the formula
*
*   Cell formula: =CUSTOMFUNC(3, 'Expenses'!B7)
*   Usage:        var ranges = GetParamRanges("CustomFunc");
*   Result:       ranges[0]: null
*                 ranges[1]: range object for cell B7 in sheet Expenses
* 
*   Cell formula: =sqrt(4+myfunction(A1:C3))
*   Usage:        var ranges = GetParamRanges("MyFunction");
*   Result:       ranges[0]: range object for cells A1 through C3 in the sheet containing the formula
*
*   Cell formula: =CustomFunc(A1+A2, A1, A2)
*   Usage:        var ranges = GetParamRanges("CustomFunc");
*   Result:       ranges[0]: null
*                 ranges[1]: range object for cell A1 in the sheet containing the formula
*                 ranges[2]: range object for cell A2 in the sheet containing the formula
*
* @param {funcName} The name of the custom function (string, case-insensitive)
* @return The range(s) referenced by the parameters to the call
*/
function GetParamRanges(funcName) {
  var ourSheet = SpreadsheetApp.getActiveSheet();
  var formula = SpreadsheetApp.getActiveRange().getFormula();
  var re = new RegExp(".+" + funcName + "\\s*\\((.*?)\\)","i");
  var ranges=[]; // array of results

  try {
    var args = formula.match(re)[1].split(/\s*,\s*/) // arguments to custom function, separated by commas
    // if there are no args it fails and drops out here

    for (var i=0; i<args.length; i++) {
      var arg=args[i].split('!'); // see if arg has a sheet name
      try {
        if (arg.length == 1) { // if there's no sheet name then use the whole arg as the range definition
          ranges[i] = ourSheet.getRange(arg[0]);
        }
        else { // if there's a sheet name, use it (after removing quotes around it)
          var sheetName=arg[0].replace(/'/g, '');
          var otherSheet=SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
          ranges[i] = otherSheet.getRange(arg[1]);
        }
      }
      catch(e) { // assume it couldn't be identified as a range for whatever reason
        ranges[i]=null;
      }
    }
  }
  catch(e) {}

  return ranges
}

1
一点也不差,但是请指出,这使假设custion函数仅在公式中使用一次。Fi我必须使用类似论坛的内容:= CountCcolor(B5:B38,$ A40)/ 2 + CountCcolor(B5:B38,$ A41)/ 2 + CountCcolor(B5:B38,$ A42)/ 2 + CountCcolor(B5:B38 ,$ A43)/ 2 + CountCcolor(B5:B38,$ A44)/ 2从我的理解来看,这种方式行不通。如果我们解决它能够处理这个问题,那就太好了。
haemse

1

我通过Google Apps脚本区分了Google Spreadsheet中的两个不同的自定义函数:

  1. 需要API的人;SpreadsheetApp,(示例
  2. 一个不打任何电话的电话(例如

第一个功能几乎可以执行任何操作。除了调用电子表格服务外,它还可以调用GmailApp或Google提供的任何服务。API调用将减慢该过程。可以传递范围或通过函数检索范围,以访问所有可用方法。

第二个功能仅限于“电子表格”。在这里,人们可以利用JavaScript来重新处理数据。这些功能通常非常快。传递的范围不过是包含值的2D数组。


在您的问题中,您首先需要调用该.getColumn()方法。如上所述,这清楚地表明您需要1类自定义函数。

请参阅以下有关如何设置电子表格和表格以创建自定义功能的答案


1

几个月前,我正在研究此问题,并提出了一个非常简单的想法:创建一个新工作表,将每个单元格的名称作为其内容:单元格A1看起来像:

= arrayformula(cell("address",a1:z500))

将工作表命名为“ Ref”。然后,当您需要引用单元格作为字符串而不是内容的引用时,可以使用:

= some_new_function('Ref'!C45)

当然,您需要检查该函数是否传递了字符串(一个单元格)或一维或二维数组。如果得到一个数组,它将具有所有单元格地址作为字符串,但是从第一个单元格的宽度和高度,您可以弄清楚所需的内容。


0

我整个上午一直在努力,并看到上述非答案讨论内容的证据。有理智的我都想要传递的单元格的地址,而不是值。答案很简单。无法完成。

我发现了一些变通方法,这些变通方法取决于弄清楚哪个单元格包含该公式。很难说这是否会对以上的Senseful有所帮助。那我在做什么呢?

The data.

     [A]      [B]       [C]       [D]     [E]     [F]       [H]
[1] Name      Wins     Losses    Shots   Points   Fouls   Most recent
                                                          WL Ratio
[2] Sophia     4         2         15      7       1         0
[3] Gloria     11        3         11      6       0         0
[4] Rene       2         0         4       0       0         0
[5] Sophia     7         4         18      9       1         1.5

H列是Sophia的(胜利-PrevWins)/(亏损-PrevLosses)

(7-4)/(4-2)= 1.5

但是我们不知道Sophia以前出现在哪一行。
如果将A硬编码为name列,则可以全部使用VLOOKUP完成。在VLOOKUP之后,我得到了一些#NA(找不到名称)和#DIV0(分母为零),并用= IF(IF(...))将其包装起来,以在这些条件下显示更多可口的文本。现在,我有一个巨大的表情,难以处理且难以维护。所以我想要宏扩展(不存在)或自定义函数。

但是当我创建一个助手SubtractPrevValue(cell)时,它收到的是“ 7”而不是B5。没有内置方法可以从传递的参数中获取单元格或范围对象。
如果我让用户用手工输入双引号的单元格名称,那么我可以做到... SubtractPrevValue(“ B5”)。但这确实阻碍了复制/粘贴和相对细胞功能。

但是后来我找到了解决方法。

SpreadsheetApp.getActiveRange()是包含公式的单元格。这就是我真正需要知道的。行号。以下函数采用NUMERIC列号,并减去该列中的上一个匹配项。

function SubtractPrevValue(colNum) 
{
  var curCell = SpreadsheetApp.getActiveRange();
  var curSheet = curCell.getSheet();
  var curRowIdx = curCell.getRowIndex();
  var name = curSheet.getRange(curRowIdx, 1).getValue();  // name to match
  var curVal =  curSheet.getRange(curRowIdx, colNum).getValue();

  var foundRowIdx = -1;
  for (var i=curRowIdx-1;i>1;i--)
  { 
    if (curSheet.getRange(i, 2).getValue() == name)
    {
      return curVal - curSheet.getRange(i, colNum).getValue();
    }
  }
  return curVal;  //default if no previous found
}

但是后来我发现这真的很慢。在显示“ Thinking ...”(思考...)时延迟一到两秒钟,因此我回到了难以理解的,难以维护的工作表公式。


0

除了提供“ Range”对象外,Google Developers几乎传递了一个随机数据类型。因此,我开发了以下宏来打印输入的值。

function tp_detail(arg)
{
    var details = '';

    try
    {
        if(typeof arg == 'undefined')
           return details += 'empty';

        if (typeof arg !== 'object')
        {
            if (typeof arg == 'undefined')
                return details += 'empty';

            if (arg.map)
            {
                var rv = 'map: {';
                var count = 1;
                for (var a in arg)
                {
                    rv += '[' + count + '] ' + tp_detail(a);
                    count = count + 1;
                }
                rv += '}; '
                return rv;
            }
            return (typeof arg) + '(\'' + arg + '\')';
        }


        if (arg instanceof Array)
        {
            details += 'arr[' + arg.length + ']: {';
            for (var i = 0; i < arg.length; i++)
            {
                details += '[' + i + '] ' + tp_detail(arg[i]) + ';';
            }
            details += '} ';
        }
        else
        {

            var className = arg.constructor.name;
            return className + '(' + arg + ')';
        }
    }
    catch (e)
    {
        var details = '';
        details = 'err : ' + e;
    }


    return details;
}

0

这是我以前的答案的汇编

**
 *
 * @customfunction
 **/
function GFR(){
  var ar = SpreadsheetApp.getActiveRange();
  var as = SpreadsheetApp.getActive();
  return ar.getFormula()
    .replace(/=gfr\((.*?)\)/i, '$1')
    .split(/[,;]/)
    .reduce(function(p, a1Notation){
      try{
        var range = as.getRange(a1Notation);
        p.push(Utilities.formatString(
          'Is "%s" a Range? %s', 
          a1Notation,
          !!range.getDisplayValues
        ));
      } catch(err){
        p.push([a1Notation + ' ' + err.message]);
      }
    return p;
  },[]);
}

它采用当前公式并检查它是否为范围。

=GFR(A1:A5;1;C1:C2;D1&D2) 退货

|Is "A1:A5" a Range? true|
|1 Range not found       |
|Is "C1:C2" a Range? true|
|D1&D2 Range not found   |

对于完整的合成,TC可以调用这样的内容

var range = as.getRange(a1Notation);
var column = range.getColumn();

-1

创建函数并将范围作为参数传递:

function fn(input) {
    var cell = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange(SpreadsheetApp.getActiveRange().getFormula().match(/=\w+\((.*)\)/i)[1].split('!'));
    // above ... cell is "range", can convert to array?
    var sum=0;
    for (var i in cell.getValues() ){
        sum = sum + Number(cell.getValues()[i]);
    }  
    return sum;
}

电子表格中的用法:

=fn(A1:A10)

它将显示为sum(A1:A10)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.