Як передати діапазон у спеціальну функцію в електронних таблицях Google?


44

Я хочу створити функцію, яка займає діапазон ... щось на кшталт:

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

Клітина буде посилатися на неї, використовуючи:

=myFunction(A1:A4)

Проблема полягає в тому, що коли я намагаюся це зробити, параметр здається, що він передає значення лише функції. Таким чином, я не можу використовувати жоден із методів діапазону, таких як getColumn(). Коли я намагаюся це зробити, він видає таку помилку:

помилка: TypeError: Неможливо знайти функцію getColumn в об’єкті 1,2,3.

Як я можу надсилати фактичний діапазон, а не лише значення, до однієї зі своїх спеціальних функцій?

Відповіді:


22

Тому я довго і наполегливо шукав гарну відповідь на це, і ось що я знайшов:

  1. параметр немодифікованого діапазону передає значення комірок у діапазоні, а не сам діапазон (як пояснив Гергелі), наприклад: коли ви це робите=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

@EmersonFarrugia Я маю на увазі можливість листів google автоматично оновлювати відносні посилання на клітинки, коли ви копіюєте функції з однієї комірки в іншу
Натан Ханна

9

rangeтрактується як 2d масив javascript. Ви можете отримати кількість рядків із range.lengthта кількість стовпців range[0].length. Якщо ви хочете отримати значення з рядка rі стовпця cвикористання: range[r][c].


Я намагаюся з’ясувати, що таке перша колонка в діапазоні. Так , наприклад, в діапазоні C1: F1, я хочу знати , що починається діапазон на колонці С
Senseful

@Senseful Якщо ви будете використовувати свою функцію у комірці типу:, =myFunction(C1:F1)то всередині функції range[0][0]поверне значення C1, range[0][1]поверне значення D1, range[0][2]поверне значення E1тощо. Чи зрозуміло це?
Ліпіс

Я думаю, що я не пояснюю себе чітко ... подивіться на вашу відповідь на це питання . Ви рекомендували мені використовувати =weightedAverage(B3:D3,$B$2:$D$2), я хотів би зробити функцію більш доказовою для помилок, не надсилаючи другий аргумент (тобто я хочу викликати це як =weightedAverage(B3:D3)), а потім мати код автоматично знати, що діапазон починається з B і закінчується у D, тож отримує відповідні значення з 2-го ряду. Примітка: значення "2" може бути жорстко кодоване, але "B" не повинно бути жорстко кодоване.
Розсудливий

1
@Senseful В цілому я проти твердо кодованих речей, і я думаю, що зрозуміліше, якщо середньозважений показник займає два параметри. Тож, якщо ви збираєтеся отримати жорсткий код, то ви можете зробити багато інших речей. Зараз я не можу знайти, як ми можемо вийти Bіз заданого range. Я б запропонував вам пройти посібник із початку роботи ( goo.gl/hm0xT ), якщо ви ще цього не зробили, щоб отримати уявлення про те, як можна грати з діапазонами та клітинками.
Ліпіс

6

При передачі Rangeфункції "Електронна таблиця Google" рамка виконується paramRange.getValues()неявно, і ваша функція отримує значення в діапазоні у вигляді двовимірного масиву рядків, чисел або об'єктів (подібних Date). RangeОб'єкт не передається вашої користувальницької функції електронної таблиці.

TYPEOF()Нижче функція покаже вам , які дані ви отримуєте в якості параметра. Формула

=TYPEOF(A1:A4)

буде називати сценарій так:

функція izračunaCell () {
  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')
    значення поверненняof;

  var деталі = '';
  if (значення instanceof Array) {
    деталі + = '[' + value.length + ']';
    if (значення [0] екземпляр масиву)
      деталі + = '[' + значення [0]. довжина + ']';
  }

  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), не =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

Я розкрив чудову ідею у відповіді користувача79865 , щоб змусити її працювати в більшій кількості випадків і передавати декілька аргументів у користувацьку функцію.

Щоб скористатись нею, скопіюйте код нижче, а потім у спеціальній функції виклику, 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
зовсім не погано, але, будь ласка, зазначте, що це робить припущення, що функція згоряння використовується лише один раз у формулі. 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 виокремлюються дві різні користувацькі функції:

  1. Той, що вимагає API; SpreadsheetApp, ( приклад )
  2. Той, який не здійснює жодних дзвінків ( наприклад, )

Першу функцію здатний виконувати практично все. Окрім виклику служби електронних таблиць, він може закликати GmailApp або будь-яку службу, надану Google. Дзвінки API пригальмують процес. Діапазон можна передати або отримати через функцію для доступу до всіх доступних методів.

Друга функція обмежена лише "Електронною таблицею". Тут можна використовувати JavaScript для переробки даних. Ці функції, як правило, дуже швидкі. Пройдений діапазон - це не більше ніж 2D-масив, що містить значення.


У своєму запитанні ви починаєте з виклику .getColumn()методу. Це чітко вказує на те, що вам потрібна спеціальна функція типу 1, як описано вище.

Дивіться наступну відповідь про те, як встановити електронну таблицю та аркуш, створити власну функцію.


1

Я працював над цим кілька місяців тому і придумав дуже простий хитрість: створити новий аркуш з назвою кожної комірки як її вмісту: Стільниця А1 може виглядати так:

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

Назвіть аркуш «Реф. Тоді, коли вам потрібна посилання на комірку як рядок замість вмісту, ви використовуєте:

= some_new_function('Ref'!C45)

Звичайно, вам потрібно буде перевірити, чи передається функція рядок (одна комірка) або 1D або 2D масив. Якщо ви отримаєте масив, він матиме всі адреси комірок як рядки, але з першої комірки та ширини та висоти ви зможете зрозуміти, що вам потрібно.


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 - це Софія (Виграє - PrevWins) / (Втрати - PrevLosses)

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

Але ми не знаємо, у якому рядку раніше з'являлася Софія.
Це все можна зробити за допомогою VLOOKUP, якщо ви введете жорсткий код A як стовпець імені. Після VLOOKUP я отримав #NA (ім’я не знайдено) та # DIV0 (нульовий знаменник) і обернув його = IF (Якщо (...)), щоб показати більш приємний текст у цих умовах. Тепер у мене було жахливо велике вираження, яке було непростим і нездійсненним. Тому я хотів розширення макросу (не існує) або спеціальних функцій.

Але коли я зробив помічник SubtractPrevValue (комірка), він отримував "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
}

Але потім я виявив, що це дійсно ДІЙСНО повільно. Одна і дві секунди затримуються під час відображення "Думаю ..." Тому я повернувся до масово нерозбірливої, незрозумілої формули робочого аркуша.


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   |

Для повного складу ТС може назвати щось подібне

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.