Обновление исторических диапазонов при добавлении строки / столбца в Google App Script

#google-apps-script #google-sheets

#google-apps-script #google-sheets

Вопрос:

введите описание изображения здесь

Я знаю, что это довольно просто для опытного разработчика Google App Script. Я новичок в Google App Script и пробую это с 2 дней, поэтому я пришел сюда.

Позвольте мне объяснить сценарий, у нас есть надстройка Google Sheet. Когда пользователь выполняет какой-либо запрос с помощью нашего дополнения, мы сохраняем идентификатор запроса [столбец A], название листа [столбец B] и обновленный диапазон ячеек [столбец C] на скрытом листе, как показано на рисунке.

Теперь мы хотим обновить эти диапазоны, если пользователь добавит / удалит столбец / строку с листа.

например, у нас есть диапазон J4: k14 на листе. например, пользователь добавляет столбец слева от C, затем диапазон J4: K14 сдвигается на K4: L14.

 function onChange(e) {
 if (['EDIT', 'INSERT_ROW', 'INSERT_COLUMN', 'REMOVE_ROW', 'REMOVE_COLUMN'].includes(e.changeType)) {
    
    var range = SpreadsheetApp.getActiveRange();
    var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    var querySheet = activeSpreadsheet.getSheetByName("OurHiddenSheet");
    // First I want to read C column to get values. that will be assigned in **range**.
    var addresses = Sheets.Spreadsheets.Values.get(querySheet.getSheetId(), range);
    // Since addresses contain all ranges, I can create RangeList from this.
    var rangeList = activeSpreadsheet.getRangeList(addresses);

    // Is there any function available in Google App Script, if the Range was impacted by the operation or not?
    

  }
}
  

Как прочитать весь столбец B и C и отфильтровать на основе названия текущего листа и диапазона?

Я хочу знать, доступны ли какие-либо функции, позволяющие узнать, повлияла ли операция на диапазон?

Комментарии:

1. Я должен извиниться за мой плохой английский. К сожалению, я не могу понять Now We want to update these ranges if user add/remove column/row from the sheet. e.g we have range J4:k14 in the sheet. e.g User add column left of C, then the J4:K14 range shift to K14:L14. How to read the entire B and C column and filter based on current sheet name and range? . Могу ли я спросить вас о деталях этого?

2. @Tanaike извините, что произошла ошибка. Обновленный диапазон будет K4: L14. Данные, которые находились в позиции J4: K14, новой позицией будут K4: L14 из-за добавления столбца в C

3. Спасибо за ответ. Я должен снова извиниться за мой плохой английский. К сожалению, из вашего обновленного вопроса я все еще не могу понять вашу текущую проблему с вашим сценарием и вашей целью. В частности, я не могу представить ситуацию из e.g we have range J4:k14 in the sheet. e.g User add column left of C, then the J4:K14 range shift to K4:L14. How to read the entire B and C column and filter based on current sheet name and range? .

4. Откройте лист, поместите некоторые данные в диапазон D2: E2. Добавьте столбец слева в столбец C. Данные, которые мы добавили в диапазон D2: E2, это новая позиция E2: F2 .

5. Спасибо за ответ. Я должен снова извиниться за мой плохой английский. К сожалению, я не могу понять взаимосвязь между Open a sheet Put some data in D2:E2 range. Add a column left to C column. The data which we added D2:E2 range, it's new position is E2:F2 . и вашим изображением и сценарием.

Ответ №1:

Лучший способ — хранить данные как developerMetadata. Например, для данных первой строки ( NewSheet!J4:K14 ) вы можете сохранить метаданные в новой таблице строки!4:4 и новой таблице столбца!J: J, где key может быть uuid запроса, а value — количество строк (10) и количество столбцов (2) соответственно.

Смотрите:

Комментарии:

1. @ThaMaster благодарим вас за предложение developerMetaData, но ограничение в 30000 символов создаст для нас проблему. У нас есть еще много данных, которые необходимо сохранить с каждым действием, выполняемым пользователем в нашем дополнении.

2. @jagad89 Затем просто сохраните ссылки на диапазоны, как я показал. Сохраните остальное в своем скрытом листе. Кроме того, это 30k 30k на лист

3. мы обновили наш подход. Когда мы генерируем какой-либо диапазон, мы даем ему имя. И формулы набора получают имя листа и диапазон из этого именованного диапазона. Таким образом, всякий раз, когда пользователь выполняет операцию, namedrange обновляется, и это отражается в нашем журнале запросов.

4. @jagad89 Не стесняйтесь добавлять ответ.

Ответ №2:

Решение

Нет встроенной функции, которая позволяет узнать, был ли изменен диапазон. Но вы можете легко создать алгоритм обнаружения столкновений, поскольку это простое 2D-пространство:

 /**
 * Detects the collision between to ranges specified in A1 Notation
 *
 * @param {string} a - The first range in A1 Notation.
 * @param {string} b - The second range in A1 Notation.
 *
 * @returns {boolean} - true if the two ranges overlaps false otherwise
 */
function collision(a,b) {
  var _a = SpreadsheetApp.getActiveSpreadsheet().getRange(a);
  var _b = SpreadsheetApp.getActiveSpreadsheet().getRange(b);
  
  var _ax = _a.getColumn();
  var _ay = _a.getRow();
  var _awidth = _a.getLastColumn() - _ax;
  var _aheight = _a.getLastRow() - _ay;
  
  var _bx = _b.getColumn();
  var _by = _b.getRow();
  var _bwidth = _b.getLastColumn() - _bx;
  var _bheight = _b.getLastRow() - _by;
  
  return _ax < _bx   _bwidth amp;amp; _ax   _awidth > _bx amp;amp; _ay < _by   _bheight amp;amp; _ay   _aheight > _by;
}
  

С помощью этой функции вы можете создать, switch case который обновляет ваши ссылки на диапазон в соответствии с Event type координатами перекрытия и активного диапазона.

Например, в случае «INSERT_COLUMN» вы можете обновить следующим образом:

    var values;
   switch(e_type) {
     case "INSERT_COLUMN":
       values = querySheet.getRange("A2:C2")
                .getValues()
                .map((row) => {
                     let r = querySheet.getRange(row[2]);
                     // If the active range is outside our rangelist we don't have to update
                     if (row[1] == sheetName amp;amp; range.getColumn() < r.getLastColumn()) {
                          let width = range.getLastColumn() - range.getColumn()   1;
                          // When overlapping you should check where the active range start wither to offset the whole range or to extend its width.
                          if (collision(row[2], range.getA1Notation())) {
                                 if (range.getColumn() <= r.getColumn()) {
                                        row[2] = getShiftOffsetColumn(r, width);
                                 } else {
                                        row[2] = getShiftExtendedColumn(r, width);
                                 }
           
                           } else {
                                 // No collision, just offset.
                                 row[2] = getShiftOffsetColumn(r, width);
                           }
                     }
                     return row;
       });
       
       break;
     case "REMOVE_COLUMN":

       [...]
     
     default:
       // Exception handling
   }
   querySheet.getRange("A2:C2").setValues(values); //updates the values in hidden sheet

[...]

// --- Support functions ---

// This function handles the offset
function getShiftOffsetColumn(r, width) {
   return SpreadsheetApp.getActiveSheet().getRange(r.getRow(), r.getColumn()   width, r.getLastRow() - r.getRow()   1, r.getLastColumn() - r.getColumn()   width).getA1Notation();
}

// This function handles the extension
function getShiftExtendedColumn(r, width) {
  return SpreadsheetApp.getActiveSheet().getRange(r.getRow(), r.getColumn(), r.getLastRow() - r.getRow()   1, r.getLastColumn() - r.getColumn()   1   width).getA1Notation();
}