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


44

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

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

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

=myFunction(A1:A4)

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

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

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

回答:


22

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

  1. 変更されていない範囲パラメーターは、範囲自体ではなく範囲内のセルのを渡します(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));


「インテリジェントに範囲参照を他のセルにコピーする」とはどういう意味ですか?
エマーソンファルギア

命の恩人。動作します、ありがとう。
ジティンパヴィスラン

@EmersonFarrugia私はあなたが一つのセルから別の関数をコピーし、自動更新、相対セル参照するには、Googleシートの能力に言及しています
ネイサンハンナ

9

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


範囲の最初の列が何であるかを見つけようとしています。たとえば、範囲C1で:F1は、私は列Cにその範囲の開始を知りたい
Senseful

あなたのようなセルに、あなたの関数を使用します場合@Senseful: =myFunction(C1:F1)、その後、関数内range[0][0]の値を返しますC1range[0][1]の値を返しますD1range[0][2]の値を返すE1など。は、その明確ですか?
リピス

私は自分自身を明確に説明していないと思います... この質問に対するあなたの答えを見てください。を使用することをお勧め=weightedAverage(B3:D3,$B$2:$D$2)します.2番目の引数を送信する必要がないことで関数をよりエラー防止します(つまり=weightedAverage(B3:D3)、として呼び出したい)、そして範囲がBで始まり終了することをコードに自動的に知らせるD。したがって、2番目の行から対応する値を取得します。注:値「2」はハードコーディングできますが、「B」はハードコーディングしないでください。
意味のある

1
@Senseful一般的に、私はハードコーディングされたものに反対です。そして、加重平均が2つのパラメータを取る場合、より明確だと思います。したがって、ハードコーディングしようとしているのであれば、他にもできることがたくさんあります。どうやっBて与えられたものから抜け出すことができるのか今はわかりませんrange。範囲とセルをどのように再生できるかについてのアイデアを得るために、まだ開始していない場合は、入門ガイド(goo.gl/hm0xT)に目を通すことをお勧めします。
リピス

6

RangeGoogleスプレッドシート関数に渡すと、フレームワークがparamRange.getValues()暗黙的に実行され、関数はRangeの値を文字列、数値、またはオブジェクト(などDate)の2次元配列として受け取ります。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){
  if(typeof value!== 'object')
    typeof値を返します。

  var details = '';
  if(valueinstof Array){
    details + = '[' + value.length + ']';
    if(value [0] instanceof Array)
      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及びその後の行、及び多数の列を-ない終了行と終了列。したがって、算術。


それは私が期待する正確な答えです。行および列。
チェン・チェン

3

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

私は、この記事でも上の次の質問に答えると思いスタックオーバーフロースプレッドシート関数にセル参照を渡す
ルベン・

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
悪くはありませんが、これは式で関数が1回だけ使用されると仮定していることを述べてください。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 Scriptを使用して、Googleスプレッドシートの2つの異なるカスタム関数を区別します。

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

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

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


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

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


1

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

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

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

= some_new_function('Ref'!C45)

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


0

私はこれまでずっと午前中に取り組んでおり、上記の非回答が議論していることの証拠を見てきました。意味があり、私は両方とも、値ではなく、渡されたセルの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

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

しかし、ヘルパーSubtractPrevValue(cell)を作成したとき、B5ではなく「7」を受け取っていました。渡された引数からセルまたは範囲オブジェクトを取得する組み込みの方法はありません。
ユーザーに二重引用符でセル名を手動で入力させると、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 ...」と表示されている間、1秒と2秒の遅延があるので、非常に判読できず、維持できないワークシート式に戻ります。


0

「範囲」オブジェクトを提供する代わりに、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

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

**
 *
 * @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)ます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.