gas

[WIP] Google Apps Script 高速化

編集中ですが、ちょっとでも参考になればと公開します。


Google Apps Script(以下、GAS)を触っています。
GASの対象はSpreadsheetで、複数のSpreadsheetから、複数のシートを参照するような作りになっているためどうしても速度がでません。
ここでは僕が実施した高速化を紹介したいと思います。

セルをシートから直接参照しない

var sheet = SpreadsheetApp.openById('SPREADSHEET_ID').getSheetByName('SHEET_NAME');
var range = sheet.getRange(ROW, COLUMN);
var value = range.getValue();

上記のようにすることでシートから値が取得できます。しかし、複数の値を取得したい時はどうでしょう?

// BAD
var sheet = SpreadsheetApp.openById('SPREADSHEET_ID').getSheetByName('SHEET_NAME');
for(var i = 0; i < 10; i++) {
  for(var j = 0; j < 10; j++) {
    var range = sheet.getRange(i, j);
    var value = range.getValue();
  }
}

もちろん上記のように取得することもできます。しかし、 getRange()getValue() はGASのAPI呼び出しに相当するため、何度も呼び出すと極端に遅くなってしまいます。
ではどうすればよいのか。次のようにしましょう。

// GOOD
var sheet = SpreadsheetApp.openById('SPREADSHEET_ID').getSheetByName('SHEET_NAME');
var range = sheet.getRange(1, 1, 10, 10);
var values = range.getValues();
for(var i = 0; i < values.length; i++) {
  var row = values[i];
  for(var j = 0; j < row.length; j++) {
    var col = row[j];
  }
}

変数 col にセルの値が入るようになります。
行数は多くなってしまいますが、GASを高速化する上ではほぼ必須のテクニックだと思います。

ループ内で無駄なAPI呼び出しをしない

例として、1つのSpreadsheetに シート1シート10 のという連続するシートがあったとします。各シートはデータは違うもののフォーマットは同じで、各シートの全ての値が欲しい、という場合を想定します。
以下のようにすると遅くなります。

// BAD
for(var i = 1; i <= 10; i++) {
  var sheet = spreadsheet.getSheetByName('シート' + i);
  var range = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn());
  var values = range.getValues();
  SOME_EXCELLENT_METHOD(values);
}

どうすればよいのか。それは getLastRow()getLastColumn() の呼び出しを減らせばいいのです。

// GOOD
var row, col;
for(var i = 1; i <= 10; i++) {
  var sheet = spreadsheet.getSheetByName('シート' + i);
  if(!row) {
    row = sheet.getLastRow();
    col = sheet.getLastColumn();
  }
  var range = sheet.getRange(1, 1, row, col);
  var values = range.getValues();
  SOME_EXCELLENT_METHOD(values);
}