google apps script sheet2jsonの高速化に挑戦

google apps script sheet2jsonの高速化に挑戦

Table of Contents

f:id:re_shikajiro:20110302140814p:image

しかだよ。

google apps scriptで書いたsheet2jsonを某所で使って頂いてるのですが、「重すぎて話にならないわ」と改善要望を頂いたのでとりあえず調査してみることにしました。

sheet2jsonを処理の流れ

f:id:re_shikajiro:20110306165104p:image

  1. まずsheetの文字が書いてあるところを範囲選択。
  2. 範囲選択した箇所を1行ずつJSONに変換。
  3. 行の中に参照(#hoge or #[]hoge)があればそのシートに移動して1の処理を繰り返す(再帰)。

処理時間を見てどこが重いのか判断する

とりあえず

Logger.log(new Date().toLocaleString());

を要所に記述して処理時間を見てみました。

sheet2json:2011/03/06 15:34:10 JST
generateSheet2Json start:2011/03/06 15:34:10 JST
 getKeyValueRanges start:2011/03/06 15:34:10 JST
 getKeyValueRanges end:2011/03/06 15:34:11 JST
 generateJsons start:2011/03/06 15:34:11 JST
   generateSheet2Json start:2011/03/06 15:34:11 JST
    getKeyValueRanges start:2011/03/06 15:34:11 JST
    getKeyValueRanges end:2011/03/06 15:34:13 JST
    generateJsons start:2011/03/06 15:34:13 JST
      generateSheet2Json start:2011/03/06 15:34:13 JST
       getKeyValueRanges start:2011/03/06 15:34:13 JST
       getKeyValueRanges end:2011/03/06 15:34:22 JST
       generateJsons start:2011/03/06 15:34:22 JST
         generateSheet2Json start:2011/03/06 15:34:42 JST
          getKeyValueRanges start:2011/03/06 15:34:42 JST
          getKeyValueRanges end:2011/03/06 15:35:08 JST
          generateJsons start:2011/03/06 15:35:08 JST
          generateJsons end:2011/03/06 15:35:09 JST
以下略

getKeyValueRangesの時間が長いですね。1シートのJSON生成処理に約10秒かかっていて、その内の9秒が範囲選択です。(最大で30秒)

どうやら範囲選択にけっこう時間がかかってるみたいです。しかも再帰するたびに呼ばれているのでオワタ感じです。

原因

sheetの中からJSONにするセルを範囲選択する必要があるのですが、今はloop処理で「隣(下)に文字があるか?」を見ながら進んでいって範囲を決めています。ここらへんが重そうですね。

対策

対象となるセルをあらかじめ名前付けして、getRangeByName()で一発で範囲を指定する。

欠点

シート作成時に範囲選択しなくてはいけないので、うっかり指定を忘れてsheetと出力されたjsonで不一致が起きそうです。

ソースコード

変更前は while でループ処理やってました。

var ranges = getKeyValueRanges(sheet);
var keys = ranges.keyRange.getValues()[0];
var values = ranges.valueRange.getValues();

/* *keyとvalueの範囲を指定する。 / function getKeyValueRanges(sheet){ var range = {}; var colIndex = 1; while(true){ range = sheet.getRange(1, colIndex, 1, 1); var value = range.getValue(); if(!value){ break; } colIndex++; } var valueRange = getValueRange(sheet, colIndex); var keyRange = sheet.getRange(1, 1, 1, colIndex-1); return {keyRange:keyRange,valueRange:valueRange}; } / *valueの範囲を指定する。 */ function getValueRange(sheet,colIndex){ var range = {}; var rowIndex = 1; while(true){ range = sheet.getRange(rowIndex+1, 1, 1, 1); var value = range.getValue(); if(!value){ break; } rowIndex++; } return sheet.getRange(2, 1, rowIndex-1, colIndex-1); }

変更後

getRangeByNameにしました。

  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var ranges = ss.getRangeByName(sheet.getName());
  var keys = ranges.getValues()[0];
  var values = ranges.getValues().slice(1);

1秒未満になりました。可読性も上がったし、やったねたえちゃん。