постоянно возникающая ошибка нехватки памяти в скриптах Google Apps при копировании проверок данных из шаблона - PullRequest
4 голосов
/ 07 октября 2019

У меня есть сотни таблиц, которые были сделаны из листа шаблона. Все они имеют одинаковое количество / имя листов, строк, столбцов и т. Д. *

Я добавил некоторые проверки данных в шаблон. Я хочу скопировать проверки данных из шаблона в каждую из таблиц. У меня есть код, и он работает, но он выдает ошибку памяти.

Он всегда выдает ошибку - меняется только то, сколько электронных таблиц назначения он обработал, прежде чем выдает ошибку. Иногда он обрабатывает 4 таблицы до того, как выдает ошибку, иногда 50, иногда больше / меньше. Я не могу понять, почему.

Я сократил свой код до рабочего образца. Я не могу поделиться исходными файлами, но это просто обычные таблицы с 5 листами / вкладками и различными проверками данных. Если это имеет значение, проверки данных используют именованные диапазоны. Например: =REGEXMATCH(LOWER(google_drive_url) , "^https:\/\/drive\.google\.com\/drive\/folders\/[a-z0-9_-]{33}$").

Я прокомментировал приведенный ниже код, но вот резюме:

  1. Получите электронную таблицу шаблона и кешируйте в ней все проверки данных
  2. Пройдите через каждый пункт назначения. Электронная таблица:
    1. Просмотрите все проверки данных
    2. Примените проверки данных из шаблона

Вмой реальный код у меня есть массив идентификаторов файла назначения. В целях тестирования я просто использую один файл назначения и несколько раз проверяю данные из шаблона.

function myFunction() {
  var sourceFileID = "1rB7Z0C615Kn9ncLykVhVAcjmwkYb5GpYWpzcJRjfcD8";
  var destinationFileID = "1SMrwTuknVa1Xky9NKgqwg16_JNSoHcFTZA6QxzDh7q4";

  // get the source file
  var sourceSpreadsheet = SpreadsheetApp.openById(sourceFileID);
  var sourceDataValidationCache = {};

  // go through each sheet and get a copy of the data validations
  // cache them for quick access later
  sourceSpreadsheet.getSheets().forEach(function(sourceSheet){
    var sheetName = sourceSheet.getName();

    // save all the data validations for this sheet
    var thisSheetDataValidationCache = [];

    // get the full sheet range
    // start at first row, first column, and end at max rows and max columns
    // get all the data validations in it
    // go through each data validation row
    sourceSheet.getRange(1, 1, sourceSheet.getMaxRows(), sourceSheet.getMaxColumns()).getDataValidations().forEach(function(row, rowIndex){
      // go through each column
      row.forEach(function(cell, columnIndex){
        // we only need to save if there is a data validation
        if(cell)
        {
          // save it
          thisSheetDataValidationCache.push({
            "row" : rowIndex + 1,
            "column" : columnIndex + 1,
            "dataValidation" : cell
          });
        }
      });
    });

    // save to cache for this sheet
    sourceDataValidationCache[sheetName] = thisSheetDataValidationCache;
  });

  // this is just an example
  // so just update the data validations in the same destination numerous times to show the memory leak
  for(var i = 0; i < 100; ++i)
  {
    // so we can see from the log how many were processed before it threw a memory error
    Logger.log(i);

    // get the destination
    var destinationSpreadsheet = SpreadsheetApp.openById(destinationFileID);

    // go through each sheet
    destinationSpreadsheet.getSheets().forEach(function(destinationSheet){
      var sheetName = destinationSheet.getName();

      // get the full range and clear existing data validations
      destinationSheet.getRange(1, 1, destinationSheet.getMaxRows(), destinationSheet.getMaxColumns()).clearDataValidations();

      // go through the cached data validations for this sheet
      sourceDataValidationCache[sheetName].forEach(function(dataValidationDetails){
        // get the cell/column this data validation is for
        // copy it, build it, and set it
        destinationSheet.getRange(dataValidationDetails.row, dataValidationDetails.column).setDataValidation(dataValidationDetails.dataValidation.copy().build());
      });
    });
  }
}

Что-то не так с кодом? Почему это вызвало бы ошибку памяти? Есть ли способ поймать / предотвратить это?

1 Ответ

1 голос
/ 09 октября 2019

Чтобы получить лучшее представление о том, что не работает, я предлагаю вам сохранить счетчик итераций, чтобы знать, сколько их проходит.

Я также только что заметил строку

sourceSheet.getRange(1, 1, sourceSheet.getMaxRows(), sourceSheet.getMaxColumns()).getDataValidations().forEach(function(row, rowIndex){

Это не очень хорошая идея, потому что getMaxRows() & getMaxColumns() получит общее количество строк влист, а не листы с данными, то есть, если ваш лист 100х100, а у вас есть данные только в первых ячейках 20х20, вы получите диапазон, который охватывает все 10000 ячеек, а затем вызов forEach означает, что вы идетечерез каждую ячейку.

Лучшим подходом к этому было бы использование getDataRange(), он вернет диапазон, который охватывает всю вашу информацию ( Документация ). При этом вы можете использовать гораздо меньший диапазон и значительно меньше ячеек для обработки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...