web-dev-qa-db-ja.com

Googleスプレッドシートのカスタム関数に範囲を渡す方法は?

私は範囲をとる関数を作成したい...のようなもの:

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

セルは以下を使用してそれを参照します。

=myFunction(A1:A4)

問題は、これを実行しようとすると、パラメーターが値を関数に渡すだけのように見えることです。したがって、 getColumn() などの Rangeメソッド は使用できません。実行しようとすると、次のエラーが表示されます。

エラー:TypeError:オブジェクト1,2,3で関数getColumnが見つかりません。

カスタム関数の1つに値だけでなく、実際の範囲を送信するにはどうすればよいですか?

43
Senseful

だから私はこれに対する良い答えを長く一生懸命探しましたが、ここに私が見つけたものがあります:

  1. 変更されていない範囲パラメーターは、範囲自体ではなく、範囲内のセルのvaluesを渡します(Gergelyの説明どおり)例:これを行う場合=myFunction(a1:a2)

  2. 関数で範囲を使用するには、まず範囲を文字列として渡し(例:=myFunction("a1:a2"))、次に関数内に次のコードを含む範囲に変換する必要があります。

    Function myFunction(pRange){
      var sheet = SpreadsheetApp.getActiveSpreadsheet();
      var range = sheet.getRange(pRange);
    }
    
  3. 最後に、範囲を渡すことの利点(範囲参照を他のセルにインテリジェントにコピーするなど)が必要で、文字列として渡す場合は、範囲をパラメーターとして受け入れ、出力するデフォルト関数を使用する必要があります使用できる文字列。私が見つけた両方の方法を以下に詳述しますが、私は2番目を好みます。

    すべてのGoogleスプレッドシートの場合:=myFunction(ADDRESS(row(A1),COLUMN(A1),4)&":"&ADDRESS(row(A2),COLUMN(A2),4));

    新しいGoogleスプレッドシートの場合:=myFunction(CELL("address",A1)&":"&CELL("address",A2));

21
Nathan Hanna

rangeは、javascriptの2D配列として扱われます。 range.lengthで行数を取得し、range[0].lengthで列数を取得できます。行rおよび列cから値を取得する場合は、range[r][c]を使用します。

9
Lipis

RangeをGoogleスプレッドシート関数に渡すと、フレームワークはparamRange.getValues()を暗黙的に実行し、関数は文字列、数値、またはオブジェクトの2次元配列(Dateなど)としてRangeの値を受け取ります。 Rangeオブジェクトは、カスタムスプレッドシート関数に渡されません。

以下のTYPEOF()関数は、パラメーターとして受け取るデータの種類を示します。式

=TYPEOF(A1:A4)

次のようなスクリプトを呼び出します。

 function calculateCell(){
 var resultCell = SpreadsheetApp.getActiveSheet()。getActiveCell(); 
 var paramRange = SpreadsheetApp.getActiveSheet()。getRange( 'A1:A4') ; 
 var paramValues = paramRange.getValues(); 
 
 var resultValue = TYPEOF(paramValues); 
 
 resultCell.setValue(resultValue); 
} 
 function TYPEOF(value){
 if(typeof value!== 'object')
 return typeof value; 
 
 var details = ' '; 
 if(value instanceof Array){
 details + =' ['+ value.length +'] '; 
 if(value [0] instanceof Array)
 details + = '[' + value [0] .length + ']'; 
} 
 
 var className = value.constructor.name; 
 return className +詳細; 
} 
6
Gregory Herendi

@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、およびnumber行、および列の数-notendrowおよび end列。したがって、算術。

3
Vidar S. Ramdal

これを行うことができます。アクティブなセル(式を含むセル)の式を解析することにより、渡された範囲への参照を取得できます。これは、カスタム関数がより複雑な式の一部としてではなく、単独で使用されることを前提としています:例えば、=myfunction(A1:C3)ではなく、=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;
}
3
user79865

User79865の answer の優れたアイデアを拡張し、より多くのケースで機能し、複数の引数がカスタム関数に渡されるようにしました。

それを使用するには、以下のコードをコピーし、カスタム関数呼び出しで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
}
2
Ian Viney

私は数ヶ月前にこれに取り組んでいて、非常に簡単なことを思いつきました:各セルの名前を内容として新しいシートを作成します:セルA1は次のようになります:

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

シートに「Ref」という名前を付けます。次に、内容ではなく文字列としてセルへの参照が必要な場合は、次を使用します。

= some_new_function('Ref'!C45)

もちろん、関数に文字列(1つのセル)または1Dまたは2D配列が渡されるかどうかを確認する必要があります。配列を取得すると、すべてのセルアドレスが文字列として取得されますが、最初のセルと幅と高さから、必要なものを見つけることができます。

1
HardScale

Google Apps Scriptを使用して、Googleスプレッドシートの2つの異なるカスタム関数を区別します。

  1. APIを呼び出すもの。 SpreadsheetApp、(
  2. 呼び出しを行わないもの、(

最初の機能は、ほとんど何でも実行できます。 Spreadsheetサービスの呼び出しとは別に、GmailAppまたはGoogleが提供するサービスを呼び出すことができます。 API呼び出しはプロセスを遅くします。範囲を渡すか、関数を介して取得して、利用可能なすべてのメソッドにアクセスできます。

2番目の機能は、「スプレッドシート」のみに限定されています。ここでは、JavaScriptを使用してデータを修正できます。これらの機能は通常非常に高速です。渡される範囲は、値を含む2D配列にすぎません。


あなたの質問では、.getColumn()メソッドを呼び出すことから始めます。これは、前述のように、タイプ1カスタム関数が必要であることを明確に示しています。

スプレッドシートとシートの設定方法については、次の answer を参照して、カスタム関数を作成してください。

1

以前の回答の私の編集があります

**
 *
 * @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();
0
contributorpw

私は午前中ずっとこれに取り組んでおり、上記の非回答が議論していることの証拠を見てきました。意味があり、私は両方とも、値ではなく、渡されたセルのADDRESSが必要です。そして答えはとても簡単です。できません。

どのセルに数式が含まれているかを把握することに依存する回避策がいくつか見つかりました。これが上記の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はソフィアの(Wins-PrevWins)/(Losses-PrevLosses)です

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

しかし、ソフィアが以前に登場した行はわかりません。
名前列としてAをハードコーディングした場合、これはすべてVLOOKUPを使用して実行できます。 VLOOKUPの後、#NA(名前が見つかりません)と#DIV0(分母ゼロ)を取得し、= IF(IF(...))でラップして、これらの条件でよりおいしいテキストを表示しました。今では手に負えず、維持できない、途方もなく大きな表現がありました。そこで、マクロ展開(存在しない)、またはカスタム関数が必要でした。

しかし、ヘルパーSubtractPrevValue(cell)を作成したとき、B5ではなく「7」を受け取っていました。渡された引数からセルまたは範囲オブジェクトを取得する組み込みの方法はありません。
ユーザーに二重引用符でセル名を手動で入力させると、SubtractPrevValue( "B5")のようになります。しかし、それは実際にコピー/貼り付けと相対的なセル機能をハムストリング化します。

しかし、その後、回避策を見つけました。

SpreadsheetApp.getActiveRange()IS式を含むセル。私が本当に知る必要があるのはそれだけです。行番号。次の関数は、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 ...」と表示されている間、1秒と2秒の遅延が発生しました。

0
Mark C

関数を作成し、範囲を引数として渡します。

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)として表示されます。

「範囲」オブジェクトを提供する代わりに、Google開発者はほぼランダムなデータ型を渡します。そこで、入力の値を出力する次のマクロを開発しました。

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
Real Name