Làm cách nào để chuyển phạm vi vào một chức năng tùy chỉnh trong Bảng tính Google?


44

Tôi muốn tạo một hàm trong phạm vi ... đại loại như:

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

Một ô sẽ tham chiếu nó bằng cách sử dụng:

=myFunction(A1:A4)

Vấn đề là khi tôi thử làm điều này, tham số có vẻ như nó chỉ truyền các giá trị cho hàm. Vì vậy, tôi không thể sử dụng bất kỳ phương thức Phạm vi nào như getColumn(). Khi tôi cố gắng làm như vậy, nó sẽ báo lỗi sau:

lỗi: TypeError: Không thể tìm thấy hàm getColumn trong đối tượng 1,2,3.

Làm cách nào tôi có thể gửi một phạm vi thực tế thay vì chỉ các giá trị cho một trong các chức năng tùy chỉnh của mình?

Câu trả lời:


22

Vì vậy, tôi đã tìm kiếm rất lâu để có câu trả lời hay cho vấn đề này và đây là những gì tôi đã tìm thấy:

  1. một tham số phạm vi chưa sửa đổi chuyển vào các giá trị của các ô trong phạm vi, chứ không phải chính phạm vi đó (như Gergely đã giải thích) ex: khi bạn làm điều này=myFunction(a1:a2)

  2. để sử dụng một phạm vi trong hàm của bạn, trước tiên bạn cần chuyển phạm vi dưới dạng một chuỗi (ví dụ =myFunction("a1:a2"):), sau đó biến nó thành một phạm vi với mã sau bên trong hàm:

    Function myFunction(pRange){
      var sheet = SpreadsheetApp.getActiveSpreadsheet();
      var range = sheet.getRange(pRange);
    }
    
  3. Cuối cùng, nếu bạn muốn lợi ích của việc truyền trong một phạm vi (như sao chép thông minh các tham chiếu phạm vi sang các ô khác) VÀ bạn muốn chuyển nó thành một chuỗi, bạn phải sử dụng một hàm mặc định chấp nhận một phạm vi làm tham số và xuất ra một chuỗi bạn có thể sử dụng. Tôi chi tiết cả hai cách tôi đã tìm thấy dưới đây, nhưng tôi thích cách thứ 2.

    Đối với tất cả các bảng tính google: =myFunction(ADDRESS(row(A1),COLUMN(A1),4)&":"&ADDRESS(row(A2),COLUMN(A2),4));

    Đối với Google Sheets mới: =myFunction(CELL("address",A1)&":"&CELL("address",A2));


Bạn có thể giải thích rõ hơn về việc "sao chép thông minh phạm vi tham chiếu đến các ô khác" nghĩa là gì không?
Emerson Farrugia

Phao cứu sinh. Nó hoạt động, cảm ơn bạn.
Jithin Pavithran

@EmersonFarrugia Tôi đang đề cập đến khả năng google sheet tự động cập nhật các tham chiếu ô tương đối khi bạn sao chép các chức năng từ ô này sang ô khác
Nathan Hanna

9

rangeđược coi là mảng 2d của javascript. Bạn có thể nhận được số lượng hàng với range.lengthvà số lượng cột với range[0].length. Nếu bạn muốn nhận giá trị từ hàng rvà cột, hãy csử dụng : range[r][c].


Tôi đang cố gắng tìm hiểu cột đầu tiên trong phạm vi là gì. Ví dụ: trong phạm vi C1: F1, tôi muốn biết rằng phạm vi bắt đầu trên cột C.
Senseful

@Senseful Nếu bạn sẽ sử dụng hàm của mình trong một ô như : =myFunction(C1:F1), thì bên trong hàm range[0][0]sẽ trả về giá trị của C1, range[0][1]sẽ trả về giá trị của D1, range[0][2]sẽ trả về giá trị của E1, v.v. Điều đó có rõ không?
Lipis

Tôi nghĩ rằng tôi không giải thích rõ ràng về bản thân mình ... hãy nhìn vào câu trả lời của bạn cho câu hỏi này . Bạn khuyên tôi nên sử dụng =weightedAverage(B3:D3,$B$2:$D$2), tôi muốn làm cho hàm thêm bằng chứng lỗi bằng cách không phải gửi đối số thứ 2 (tức là tôi muốn gọi nó là =weightedAverage(B3:D3)), và sau đó có mã tự động biết rằng phạm vi bắt đầu tại B và kết thúc tại D, do đó, nó nhận được các giá trị tương ứng từ hàng thứ 2. Lưu ý: giá trị "2" có thể được mã hóa cứng, nhưng "B" không nên được mã hóa cứng.
Ý thức

1
@Senseful Nói chung tôi chống lại những thứ được mã hóa cứng và tôi nghĩ sẽ rõ ràng hơn nếu trung bình có trọng số sẽ mất hai thông số. Vì vậy, nếu bạn sắp sửa mã cứng thì có nhiều thứ khác mà bạn có thể làm. Tôi không thể tìm thấy ngay bây giờ làm thế nào chúng ta có thể Bthoát khỏi sự cho trước range. Tôi sẽ đề nghị bạn xem qua hướng dẫn bắt đầu ( goo.gl/hm0xT ), nếu bạn chưa có, để có ý tưởng về cách bạn có thể chơi với các phạm vi và ô.
Lipis

6

Khi chuyển một hàm Rangecho bảng tính Google, khung sẽ thực thi paramRange.getValues()ngầm và hàm của bạn nhận các giá trị trong Phạm vi dưới dạng một mảng 2 chiều của chuỗi, số hoặc đối tượng (như Date). Đối Rangetượng không được chuyển đến chức năng bảng tính tùy chỉnh của bạn.

Các TYPEOF()chức năng dưới đây sẽ cho bạn biết những gì loại dữ liệu bạn nhận được là tham số. Công thức

=TYPEOF(A1:A4)

sẽ gọi kịch bản như thế này:

hàm notifyCell () {
  var resultCell = SpreadsheetApp.getActiveSheet (). getActiveCell ();
  var paramRange = SpreadsheetApp.getActiveSheet (). getRange ('A1: A4');
  var paramValues ​​= paramRange.getValues ​​();

  var resultValue = TYPEOF (paramValues);

  resultCell.setValue (resultValue);
}
hàm TYPEOF (giá trị) {
  if (typeof value! == 'object')
    trả về giá trị typeof;

  chi tiết var = '';
  if (giá trị thể hiện mảng) {
    chi tiết + = '[' + value.length + ']';
    if (value [0] instanceof Array)
      chi tiết + = '[' + giá trị [0] .length + ']';
  }

  var className = value.constructor.name;
  trả về className + chi tiết;
}

3

Thay vào đó, lấy cảm hứng từ nhận xét từ @Lipis, bạn có thể gọi hàm của mình với tọa độ hàng / cột làm tham số bổ sung:

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

Ở dạng này, công thức có thể dễ dàng được sao chép (hoặc kéo) vào các ô khác và các tham chiếu ô / phạm vi sẽ được điều chỉnh tự động.

Chức năng Script của bạn sẽ cần được cập nhật như sau:

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

Lưu ý rằng các getRangechức năng mất startRow, startColumnvà sau đó số hàng, và số lượng các cột - không chấm dứt hàng và cuối cột. Do đó, các mỹ phẩm.


Đó là câu trả lời chính xác mà tôi mong đợi. Hàng và cột.
Jianwu Chen

3

Điều này có thể được thực hiện . Người ta có thể có được một tham chiếu đến phạm vi đã vượt qua bằng cách phân tích công thức trong ô hiện hoạt, đó là ô chứa công thức. Điều này làm cho giả định rằng hàm tùy chỉnh được sử dụng riêng và không phải là một phần của biểu thức phức tạp hơn: ví dụ: =myfunction(A1:C3)không =sqrt(4+myfunction(A1:C3)).

Hàm này trả về chỉ mục cột đầu tiên của phạm vi đã qua.

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

Tôi nghĩ rằng bài đăng này cũng trả lời câu hỏi sau đây về Stack Overflow : chuyển tham chiếu ô đến các chức năng của bảng tính
Rubén

2

Tôi đã mở rộng ý tưởng tuyệt vời trong câu trả lời của user79865 , để làm cho nó hoạt động cho nhiều trường hợp hơn và với nhiều đối số được truyền cho hàm tùy chỉnh.

Để sử dụng nó, sao chép mã bên dưới, và sau đó trong hàm gọi tùy chỉnh của bạn GetParamRanges()như thế này, chuyển cho nó tên hàm của bạn:

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

Đây là một ví dụ để trả về màu của một ô:

/**
* 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();
}

Đây là mã và một số giải thích thêm:

/**
* 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
không tệ chút nào, nhưng xin vui lòng nói rằng điều này làm cho giả định hàm đốt chỉ được sử dụng một lần trong công thức. Fi Tôi đã sử dụng các diễn đàn như: = 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 Từ cách tôi hiểu nó, sẽ không hoạt động theo cách này. Nếu chúng ta giải quyết nó, beeng có thể xử lý việc này thì sẽ rất tuyệt.
haemse

1

Tôi phân biệt hai chức năng tùy chỉnh khác nhau trong Bảng tính Google, thông qua Google Apps Script:

  1. Một yêu cầu API; Bảng tính ứng dụng, ( ví dụ )
  2. Một cuộc gọi không thực hiện bất kỳ cuộc gọi nào, ( ví dụ )

Chức năng đầu tiên có khả năng làm hầu hết mọi thứ. Ngoài việc gọi dịch vụ Bảng tính, nó có thể gọi GmailApp hoặc bất kỳ dịch vụ nào do Google cung cấp. Các lệnh gọi API sẽ làm chậm quá trình. Một phạm vi có thể được truyền vào hoặc truy xuất thông qua chức năng, để truy cập tất cả các phương thức có sẵn.

Hàm thứ hai chỉ giới hạn trong "Bảng tính". Ở đây người ta có thể sử dụng JavaScript để làm lại dữ liệu. Các chức năng này thường rất nhanh. Một phạm vi được thông qua, không có gì nhiều hơn các giá trị chứa mảng 2D.


Trong câu hỏi của bạn, bạn bắt đầu bằng cách gọi cho .getColumn()phương pháp. Điều này chỉ rõ rằng bạn cần một chức năng tùy chỉnh loại 1, như được mô tả ở trên.

Xem câu trả lời sau về cách đặt bảng tính và trang tính, để tạo chức năng tùy chỉnh.


1

Tôi đã làm việc về điều này một vài tháng trước và đã đưa ra một loại bùn rất đơn giản: tạo một trang tính mới với tên của từng ô như nội dung của nó: Ô A1 có thể trông như sau:

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

Đặt tên cho tờ "Ref". Sau đó, khi bạn cần một tham chiếu đến một ô dưới dạng một chuỗi thay vì nội dung, bạn sử dụng:

= some_new_function('Ref'!C45)

Tất nhiên, bạn sẽ cần kiểm tra xem hàm có được truyền qua một chuỗi (một ô) hay Mảng 1D hoặc 2D không. Nếu bạn nhận được một mảng, nó sẽ có tất cả các địa chỉ ô dưới dạng chuỗi, nhưng từ ô đầu tiên và chiều rộng và chiều cao, bạn có thể tìm ra những gì bạn cần.


0

Tôi đã làm việc trên tất cả các buổi sáng và thấy bằng chứng về những gì những người không trả lời trên thảo luận. Senseful và tôi đều muốn ĐỊA CHỈ của ô đã qua, không phải giá trị. Và câu trả lời là rất dễ dàng. Nó không thể được thực hiện.

Tôi tìm thấy một số cách giải quyết dựa trên việc tìm ra ô nào chứa công thức. Thật khó để nói nếu điều này sẽ giúp Senseful ở trên. Vậy tôi đã làm gì?

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

Cột H là Sophia (Thắng - PrevWins) / (Mất - PrevLosses)

(7 - 4) / (4 - 2) = 1,5

Nhưng chúng tôi không biết Sophia đã xuất hiện hàng nào trước đây.
Tất cả có thể được thực hiện bằng cách sử dụng VLOOKUP nếu bạn mã cứng A làm cột tên. Sau VLOOKUP, tôi đã nhận được một số #NA (không tìm thấy tên) và # DIV0 (mẫu số 0) và bọc nó bằng = IF (IF (...)) để hiển thị văn bản hợp lý hơn trong các điều kiện này. Bây giờ tôi đã có một biểu hiện lớn khủng khiếp, khó sử dụng và không thể bảo trì. Vì vậy, tôi muốn mở rộng macro (không tồn tại) hoặc các chức năng tùy chỉnh.

Nhưng khi tôi tạo một trình trợ giúp SubtractPrevValue (ô), nó đã nhận được "7" thay vì B5. Không có cách dựng sẵn để lấy các đối tượng ô hoặc phạm vi từ các đối số được truyền.
Nếu tôi bắt người dùng nhập tên ô trong dấu ngoặc kép thì tôi có thể làm điều đó ... SubtractPrevValue ("B5"). Nhưng điều đó thực sự cản trở việc sao chép / dán và các tính năng di động tương đối.

Nhưng sau đó tôi tìm thấy một cách giải quyết.

Bảng tínhApp.getActiveRange () LÀ GÓI có chứa công thức. Đó là tất cả những gì tôi thực sự cần biết. Số hàng. Hàm sau lấy số cột SỐ và trừ đi sự xuất hiện trước đó trong cột đó.

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
}

Nhưng sau đó tôi phát hiện ra rằng điều này thực sự chậm. Một và hai giây chậm trễ trong khi nó hiển thị "Suy nghĩ ..." Vì vậy, tôi trở lại công thức bảng tính không thể đọc được, không thể nhầm lẫn.


0

Thay vì cung cấp đối tượng "Phạm vi", Nhà phát triển Google chuyển khá nhiều loại dữ liệu ngẫu nhiên. Vì vậy, tôi đã phát triển macro sau để in ra giá trị của đầu vào.

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

Có phần tổng hợp các câu trả lời trước của tôi

**
 *
 * @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;
  },[]);
}

Nó có công thức hiện tại và kiểm tra nếu nó là một Phạm vi.

=GFR(A1:A5;1;C1:C2;D1&D2) trả lại

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

Đối với thành phần đầy đủ, TC có thể gọi một cái gì đó như thế này

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

-1

Tạo hàm và vượt qua phạm vi làm đối số:

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

Sử dụng trong bảng tính:

=fn(A1:A10)

Nó sẽ hiển thị giá trị như sum(A1:A10).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.