
みなさん、こんにちは!
タカハシ(@ntakahashi0505)です。
Google Apps Scriptでスプレッドシートの操作をしていて、こう感じたことはありませんか?
実行速度が遅い…遅いぞ!
スプレッドシートへの読み書きが多いと、どうやらすごく遅くなってしまいます。
エクセルVBAの場合は画面表示の更新を停止するなどをして高速化をすることができましたが
Google Apps Scriptの場合はどうすれば良いでしょうか…?
今回はGoogle Apps Scriptでスプレッドシートを操作する場合に処理速度を格段に速くする方法をお伝えしていきます。
どれくらい遅いか?
例えば、以前紹介しましたこちらの記事のスクリプト。
名刺管理サービスCAMCARDから出力した連絡先リストを取り込んで、メルマガ配信リストを更新するものです。
このスプリプトに時間を測定する処理をちょちょっと追加します。
function inportContacts(){
var start = new Date();
// 処理
var end = new Date();
var span_sec = (end - start)/1000;
Logger.log("処理時間は " + span_sec + " 秒でした" );
}
例えばCAMCARDのデータが50行あったとして、測定しながら実行してみますと

このように約42秒もかかります。
これが500件、5000件となったら…ちょっと実用に耐えないですよね。
今回はこの課題を解決していきます。
APIの呼び出し回数が多いと遅くなる
なぜこのスクリプトが遅いか?ということなのですが、ヒントはAPIの呼び出し回数にあります。
ここで言うAPIは他でもないSpreadsheetAppです。
つまり
- getActiveSpreadsheet()
- getSheetByName()
- getDataRange()
- getLastRow()
- getRange()
- getValue()
- setValue()
などを使ってスプレッドシートにアクセスをするたびにAPIが呼び出されています。
ですから例えば
if(sheetAddress.getRange(ai,4).getValue() === sheetContacts.getRange(ci,9).getValue()){
// 処理
}
このifの条件比較には一気に4つのAPI呼び出しがあります。これが入れ子状態のfor文の繰り返しの処理に含まれていていたら…Google Apps Script的にはたまったもんじゃないわけです。
お題:シート範囲を別シートにコピーする
今回はこのようなお題を使ってスプレッドシートを扱うGoogle Apps Scriptの高速化の方法をお伝えしていきます。
まずシート1にこのようなデータを用意します。

ランダムの数値が横3列×縦50行分並んでいまして、見出しを入れると全部で51行です。
これをシート2にコピーしたいと思います。
高速化を考えない場合はこのようなスクリプトになります。
function speedUp() {
var mySS=SpreadsheetApp.getActiveSpreadsheet(); //スプレッドシートを取得
/* シート1とシート2の準備 */
var sheet1=mySS.getSheetByName("シート1");
var row1=sheet1.getDataRange().getLastRow();
var sheet2=mySS.getSheetByName("シート2");
for(var i=1;i<=row1;i++){
for(var j=1;j<=3;j++){
sheet2.getRange(i,j).setValue(sheet1.getRange(i,j).getValue());
}
}
}
わかりやすさのために実行時間測定の部分は省いています。
さて、このスプリプトを実行してみますと

21.8秒もかかっていますね。
API呼び出しが多いところを確認する
遅くなる原因でいうとスプレッドシートのAPI呼び出し回数ということになります。
前半の2~8行でシート1やシート2の準備で合計4回のAPI呼び出しがありますが、なんといっても12行目ですね。
入れ子になっている二つのfor文の中に、たったの一行で4つもAPIを呼び出すメソッドが含まれています。
行数が51、列数が3、読み書きのAPI呼び出しが1周あたり4回ですから、全部で612回のAPI呼び出しが発生していることになります。
セル一つ一つに対して読み書きをするメソッドであるgetValueやsetValueを何も考えずに使いまくると必然的に遅くなってしまうということになります。
APIリクエストを減らす方法
セル一つ一つに読み書きをすると遅くなるということですから、例えばデータのある範囲全体を一気に読み書きしたらよいのではないか?
ということになります。
では、その路線で検証してみましょう。
シート範囲を二次元配列にまとめて読み書きするgetValuesとsetValues
シートの範囲をまとめて読み書きをするgetValues、setValuesというメソッドがあります。
書き方はこちらです。
二次元配列 = Rangeオブジェクト.getValues();
Rangeオブジェクト.setValues(二次元配列);
これを使えばセル一つ一つではなくて、シート範囲のデータをまとめて読んてきたり、まとめて書き出したりすることができますね。
しかしgetValuesで取得した範囲のデータは二次元配列に入り、setValuesでシートに書き出す場合は二次元配列を指定しなければいけません。
getValuesで取得したデータの二次元配列について
二次元配列…ちょっと不慣れな場合もあるかも知れませんね。
今回のシート1を二次元配列var1に格納する場合
var var1=sheet1.getDataRange().getValues();
とします。
このとき、二次元配列はどんな様子になっているかというと
var1[0]=[数字1,数字2,数字3] var1[1]=[83,47,75]; var1[2]=[43,99,27]; var1[3]=[50,56,41]; var1[4]=[0,64,79]; // 以下続く
とした場合と同じになります。
それぞれの行に対してまず一つ目の配列が用意されて、その中にそれぞれのセルの値が配列で格納されているというわけです。
ここで注意すべき点として、行も列も配列番号は「0」からスタートするということです。
1行目のデータはvar1[0]に格納され、1行目の1列目のセルはvar1[0][0]に格納されます。
getValues、setValuesを使ったスクリプトに修正
では、前述のスクリプトをgetValues、setValuesを使ったスクリプトに書き直してみましょう。
function speedUp() {
var mySS=SpreadsheetApp.getActiveSpreadsheet(); //スプレッドシートを取得
/* シート1とシート2の準備 */
var sheet1=mySS.getSheetByName("シート1");
var row1=sheet1.getDataRange().getLastRow();
var var1=sheet1.getDataRange().getValues();
var sheet2=mySS.getSheetByName("シート2");
/* 二次元配列をコピー */
var var2=var1;
/* シート2に書き出す */
sheet2.getRange(1,1,row1,3).setValues(var1);
}
…入れ子のfor文だったあたりが、12行目に集約されました。超シンプルなたったの一行になっちゃいましたね。
getRangeで範囲を指定する
15行目のgetRangeですが引数が4つあります。
一つのセルの場所を指定する場合は引数は2つでしたが、getRangeで範囲を表す方法としては、範囲の開始セルの行数、列数とともに、その範囲の行数、列数も指定をします。
getRange(開始セルの行数, 開始セルの列数, 範囲の行数, 範囲の列数)
今回の場合は、開始セルは(1,1)、範囲の行数はシート1の行数、範囲の列数は3になりますので
sheet2.getRange(1,1,row1,3).setValues(var1);
となったわけです。
Google Apps Scriptの高速化の結果
getValues、setValuesを使って高速化をしたスクリプトを実行してみますと

0.57秒…!
実行速度はなんと38倍に。メチャメチャ速くなりましたね。
まとめ
Google Apps Scriptでスプレッドシートを操作する際に処理速度を格段に速くする方法についてお伝えしました。
二次元配列の取り扱いは慣れないと少しわかりづらいところもあるかも知れませんが、プログラムの処理速度を考えると絶対に使うべきですよね。
ぜひご活用下さい。
さて、次回ですが、今回の高速化のテクニックを活用してメルマガ配信リストの更新のスクリプトを修正していきたいと思います。
どうぞお楽しみに!
連載目次:Google Apps Scriptでメルマガシステムを作っちゃおう!
- 超初心者へGoogleAppsScriptを始めるメリットをこれでもかと説明します
- 初心者でも簡単!Google Apps ScriptでGmailを操作してメールを送る方法
- 初心者でも簡単!Google Apps Scriptでドキュメントを取得して表示する方法
- Google Apps Scriptでスプレッドシートのリストをもとに宛名を差し込んだメール本文を作る
- たったの38行!Google Apps Scriptで超簡易メルマガ配信システム
- 名刺管理アプリCAMCARDの連絡先リストをスプレッドシートに取り込む
- 【GAS】名刺管理アプリの出力データを活用してメルマガ配信リストを自動更新する
- Google Apps Scriptのスプレッドシート読み書きを格段に高速化をする方法

