Google 스프레드 시트에서 맞춤 함수에 범위를 전달하는 방법은 무엇입니까?


44

범위를 취하는 함수를 만들고 싶습니다 ...

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

셀은 다음을 사용하여 참조합니다.

=myFunction(A1:A4)

문제는이 작업을 시도 할 때 매개 변수가 값을 함수에 전달하는 것처럼 보입니다. 따라서 와 같은 Range 메서드 는 사용할 수 없습니다 getColumn(). 그렇게하려고하면 다음과 같은 오류가 발생합니다.

오류 : TypeError : 개체 1,2,3에서 getColumn 함수를 찾을 수 없습니다.

사용자 정의 함수 중 하나에 값이 아닌 실제 범위를 어떻게 보낼 수 있습니까?

답변:


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));


"지능형으로 범위 참조를 다른 셀에 복사하는 것"이 ​​무엇을 의미하는지 자세히 설명 할 수 있습니까?
Emerson Farrugia

생명의 은인. 작동합니다. 감사합니다.
Jithin Pavithran

@EmersonFarrugia 한 셀에서 다른 셀로 함수를 복사 할 때 Google 시트가 상대 셀 참조를 자동 업데이트하는 기능을 말합니다.
Nathan Hanna

9

range자바 스크립트의 2D 배열로 취급됩니다. 당신이 가진 행의 수를 얻을 수 range.length와와 열 수를 range[0].length. 행 r과 열 에서 값을 얻으려면 다음을 c사용하십시오 range[r][c].


범위의 첫 번째 열이 무엇인지 찾으려고합니다. 예를 들어 C1 : F1 범위에서 범위가 C 열에서 시작한다는 것을 알고 싶습니다.
Senseful

이 같은 셀에 함수를 사용합니다 @Senseful 경우 =myFunction(C1:F1), 다음 함수 내에서 range[0][0]의 값을 반환합니다 C1, range[0][1]의 값을 반환 D1, range[0][2]값 반환 E1, 등등을 것이 분명하다?
Lipis

나는 나 자신을 명확하게 설명하고 있지 않다고 생각한다 . 이 질문에 대한 당신의 대답을보십시오 . 당신은 내가 사용하는 것이 좋습니다 것을 =weightedAverage(B3:D3,$B$2:$D$2)내가 (즉, 내가로 전화를 걸 두번째 인수를 보낼 필요하지 않음으로써 기능 이상의 오류 증거를하고 싶습니다, =weightedAverage(B3:D3)), 다음 코드가 자동으로 알고있는 것을 B와 끝 범위의 시작에서 D이므로 두 번째 행에서 해당 값을 가져옵니다. 참고 : "2"값은 하드 코딩 될 수 있지만 "B"는 하드 코딩되지 않아야합니다.
Senseful

1
@Senseful 일반적으로 나는 하드 코딩 된 것들에 반대하고 있으며 가중 평균이 두 개의 매개 변수를 취하는 것이 더 명확하다고 생각합니다. 따라서 하드 코드를 작성하려는 경우 다른 많은 작업을 수행 할 수 있습니다. 나는 현재 우리가 B주어진 것을 어떻게 벗어날 수 있는지 찾을 수 없다 range. 아직 시작 하지 않은 경우 시작 안내서 ( goo.gl/hm0xT )를 진행하여 범위와 셀을 어떻게 사용할 수 있는지에 대한 아이디어를 얻으십시오.
Lipis

6

RangeGoogle 스프레드 시트 함수에 전달하면 프레임 워크가 paramRange.getValues()암시 적으로 실행 되고 함수는 범위의 값을 문자열, 숫자 또는 객체 (예 :)의 2 차원 배열로 Date받습니다. 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 (값) {
  if (typeof value! == 'object')
    typeof 값을 반환;

  var details = '';
  if (값 instanceof 배열) {
    details + = '['+ value.length + ']';
    if (값 [0] instanceof 배열)
      details + = '['+ 값 [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함수 얻어 startRow, startColumn다음 번호 의 행 및 다수 의 열을 금지 - 최종 행과 최종 열. 따라서 산술.


그것이 내가 기대하는 정확한 답변입니다. 행과 열.
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;
}

이 게시물은 스택 오버플로 에 대한 다음 질문에 대한 답변이라고 생각합니다 . 셀 참조를 스프레드 시트 함수에 전달
Rubén

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 Script를 통해 Google Spreadsheet에서 서로 다른 두 가지 맞춤 기능을 구분합니다.

  1. 하나는 API를 호출합니다. SpreadsheetApp ( )
  2. 전화를 걸지 않는 것 ( :)

첫 번째 기능은 거의 모든 작업을 수행 할 수 있습니다. 스프레드 시트 서비스를 호출하는 것 외에도 GmailApp 또는 Google이 제공하는 모든 서비스를 호출 할 수 있습니다. API 호출로 프로세스 속도가 느려집니다. 사용 가능한 모든 메소드에 액세스하기 위해 범위를 전달하거나 함수를 통해 검색 할 수 있습니다.

두 번째 기능은 "스프레드 시트"에만 국한됩니다. 여기에서 JavaScript를 사용하여 데이터를 재 작업 할 수 있습니다. 이러한 기능은 일반적으로 매우 빠릅니다. 전달 된 범위는 값을 포함하는 2D 배열에 지나지 않습니다.


귀하의 질문에, 당신은 .getColumn()방법 을 호출하여 시작합니다 . 이것은 위에서 설명한 것처럼 유형 1 사용자 정의 기능이 필요하다는 것을 분명히 나타냅니다.

사용자 정의 함수를 작성하기 위해 스프레드 시트 및 시트를 설정하는 방법에 대한 다음 답변 을 참조하십시오 .


1

나는 몇 달 전에이 작업을 수행했으며 매우 간단한 kludge를 생각해 냈습니다. 각 셀의 이름을 내용으로 새 시트를 만듭니다 .A1 셀은 다음과 같습니다.

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

시트 이름을 "Ref"로 지정하십시오. 그런 다음 내용 대신 문자열로 셀에 대한 참조가 필요할 때 다음을 사용합니다.

= some_new_function('Ref'!C45)

물론 함수가 문자열 (한 셀) 또는 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

그러나 우리는 소피아가 이전에 어떤 행에 나타 났는지 알 수 없습니다.
A를 이름 열로 하드 코딩하면 VLOOKUP을 사용하여이 모든 작업을 수행 할 수 있습니다. VLOOKUP 후, 일부 #NA (이름을 찾을 수 없음) 및 # DIV0 (분모가 0 임)을 받고 = 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

"Range"객체를 제공하는 대신 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.