• ログインログイン
  • 新規登録新規登録

MENU

Google Apps Scriptでプログラミングを学ぶ 第6回

連載Google Apps Scriptでプログラミングを学ぶ

Google Apps Scriptは、開発環境の設定がなくてもすぐにコーディングができる優れもののサービスです。初心者でもすぐに取り掛かれる事例を用いながら、実践に耐えうるレベルまで徐々にステップアップして学習していきます。

この記事は、株式会社らしく様からの寄稿記事となります。前回までのプログラミング学習コラムに引き続き、今回は第6回目の勉強内容になります。今回からは、実践的な文法を書いていきます。

セクション1 条件分岐と繰り返しの応用文法

3章の基本文法では、条件分岐の文法としてif, if/else, if/else elseifを、繰り返しの文法としてforを学びました。これらの文法で事足りる事が多いのですが、条件分岐にはswitch、繰り返しにはwhile、do/while、for/inという表現法もあるので、応用文法として紹介します。

switch文

switch分は分岐が多いときに便利な多重分岐の構文です。if/else if/elseを使って多重分岐させる事もできますが、もし条件分岐で評価する式が一つだけの場合、同じ式を何度も評価するのは無駄ですね。このためにswitch文が用意されています。

 書式 switch (式){
case 値1:
実行する文;
break;
case 値2:
実行する文;
break;
case 値3:
実行する文;
break;default:
実行する文;
}

switch文が実行されると、まず式の値を求めます。次にその値と一致するcaseを探します。一致するcaseがあればそのcase直後の文を実行します。一致するcaseが無ければdefaultを実行します。defaultは記述を省略できます。caseに一致せずdefaultもなければこのswitch文はスキップされます。

function printMedalColor( number ) {
switch( number ){
case 1:
Logger.log("gold medal");
break;
case 2:
Logger.log("silver medal");
break;
case 3:
Logger.log("bronze medal");
break;
case 4:
default:
Logger.log("no medal");
}
}

上記のプログラムでbreak という記述がある事に留意して下さい。break文は先の章で解説します。引数が1の場合、”gold medal”と表示して終了するにはbreakが必要です。breakを記述しないと、case 2に処理が流れてしまいます。引数が4の場合はdefaultに流れ、”no medal”の表示になります。

while文

繰り返し処理をするための基本的な制御文がwhile文です。for文では繰り返しの終了条件があらかじめ何回か分かっているケースでは便利です。しかし、それが分からないケースもあるでしょう。その場合while文を使うと見やすくなります。

 書式   while( 評価する式 ) {実行する文;}

while文が実行されると 式 を評価します。その結果がfalseと評価される場合はループ本体の文をスキップし、次の処理へ進みます。評価結果がtrueと評価される間は、{ }内の処理を実行し続けます。

function count( resultList ){
// resultList は降順でソートされている
var count=0;
while( resultList[count] >= 80 ){
count++;
}
return count;
}

この例はresultListは成績が降順にソートされた配列で、80点以上の人数が何人かをカウントするプログラムです。降順にソートされているので、79点以下になってもループするのは無駄です。この例では80点以上の間ループするという条件でwhile文を構成しています。

do/while文

whileループと似ていますが、式の評価が処理の冒頭ではなく後ろにある点が異なります。つまり、少なくとも1回は実行されるということです。

 書式   do {処理}while(評価する式);もしくはdo処理while(評価する式);

do/while文が実行されると、{ }内の処理が実行されます。次に式を評価し、falseと評価される場合はループを抜けます。trueと評価される場合は{ }内の処理を実行します。whileとは異なり、doループの最後にはセミコロンが必要です。ただし{ }で囲った場合には必要ありません。

function printArray( array ){
if( array.length == 0 ){
Logger.log("empty");
}else{
var i=0;
do{
Logger.log(array[i]);
}while( ++i < array.length )
}
}

この例の様に、配列長が0の場合を事前にチェックしているケースでは、do/whileを利用します。

for/in文

forキーワードを使いますが、通常のforとは全く異なります。3章で学んだ通り通常のfor文は配列を巡回するのに便利なものですが、これに対してfor/in文はオブジェクトのプロパティを巡回する時に使います。

 書式 for( 変数 in オブジェクト ) {処理;}

( )内の「変数」は処理で使用する変数を定義します。「オブジェクト」は評価するとオブジェクトになる式です。

function printObjectProperties( obj ){
for( var p in obj ){
Logger.log( obj[p] );
}
}

GASでは配列もオブジェクトの一種であり、インデックスがプロパティとして扱われます。従って、プロパティだけでなく配列のインデックスも変数に格納させる事も可能です。

function printArrayObject( ){
var array = new Array();
array[0] = 5;
array[1] = 31;
array[4] = 11;

for( var i in array ){
Logger.log( array[i] );
}
}

変数iには0, 1, 4の順で格納されていきます。

セクション2 その他の制御文法

このセクションでは、処理を途中でやめたり、ある処理をスキップしたりする場合に使う制御分を紹介します。

ラベル文

任意の文に「ラベル」をつける事で、プログラム中の他の場所からラベルを指定してその文にジャンプする事ができます。

 書式 識別子 : 処理

「識別子」は任意の文字列をラベル名として指定します。「処理」はラベルが指す処理です。このラベルに向かって処理をジャンプさせる事ができるのは、後述するcontinueとbreak文になります。

function label(){
mainloop : while( i < 4 ){
secondloop: while( j < 10 ){
// コード省略

continue mainloop;

// コード省略
}
}
}

このように入れ子になっているループで、一気にもうひとつ外側のループにジャンプしたい時に便利です。

break文

break文はループ、またはswitch文を終了させるための文なので、これらの中でしか使えません。ラベル無しで実行すると、最も内側のループまたはswitch文を終了します。

 書式 break;
for( var i=0; iif( array[i] == target ){
break;
}

上記の例では配列の中から目的となる要素を見つけた時に、ループを終了します。

ラベル有りで実行すると、指定されたラベルの文を終了します。

 書式 break ラベル名;

ラベル無しで実行すると、最も内側のループを先頭から再開します。ラベル有りで実行すると、指定されたラベルのループの先頭から再開します。(ラベル文の例参照)

return文

関数は必ず値を持ちます。関数中でreturn文を用いる事により、当該関数の値を指定する事ができます。従ってreturn文は関数本体でしか使えません。

 書式 return;もしくはreturn 処理や変数;

returnに「処理や変数」を指定しなくても構いません。その場合はundefinedという特殊な値が返されます。undefinedはその名の通り、「値が未定義」という意味です。

セクション3 機能別目的別にGASを分割

関数の概念、またユーザが独自に関数を定義できる事を4章で説明しました。このセクションでは、実例を挙げて関数の分割の指針を考えてみます。

あなたの行っている仕事が、ある条件のデータをスプレッドシートで抽出して解析し、解析結果をレポートで表示するという作業だとします。この3つの機能を1つの関数に書いてしまうと、非常に長いプログラムになってしまい、可読性が低下(*1)します。

*1 : 大抵のプログラムは1度書いてしまえば終わりではなく、今後もメンテナンスを重ねていくはずです。その時、読みにくいプログラムだと著しくメンテナンス工数を増大させてしまうのです。

上記の例では、次の様に機能を大別できます。

  • データの抽出
  • データの解析
  • レポートの作成

これを3つに分割します。プログラムの内容は割愛しますが、これに基づいた実装イメージは次の様になります。

関数「main()」を実行すれば、「extractData()」、「analyze()」、「makeReport()」の順番に実行されます。この例の場合、main()を見れば大まかな流れが分かり、詳細はそれぞれの関数を見れば良いという事になります。この様に、機能毎に関数を分割する事によって、可読性があがるのです。

セクション4 ライブラリ化

セクション3では関数の分割を考えました。 機能の分析を進めて行くと、スプレッドシートをまたいでよく使う共通的な機能が出てくるケースがあります。同じプログラムなのに、それぞれのスプレッドシートでプログラムするのは無駄ですね。その場合はライブラリ化すると便利です。

ライブラリ化とは、”みんなで使うものは外部で共有管理する”というような意味です。
(ライブラリは図書館という意味があります。図書館にはみんなが借りられる本が管理されていますよね。)

ライブラリ化する事で、複数のスプレッドシートから同一のプログラム(ライブラリ)を呼び出す事ができます。イメージは次のような感じです。

これまで、GASはスプレッドシートと紐づいていましたが、スプレッドシートに紐づかないGASも作成可能で、ライブラリ化する場合は、この方法で作成します。

練習

例題を用いて、関数の分割とライブラリ化の方法を学んでみましょう。

ある学校では、先生ごとにテストの合否判断をするスプレッドシートを管理していました。A先生は、算数と理科、B先生は国語と社会を教えています。
A先生とB先生のスプレッドシートとGASは以下のとおりでした。

A先生
スプレッドシート

プログラム

function judge() {
var sheet = SpreadsheetApp.getActiveSheet();

var math_grade = sheet.getRange("C1").getValue();
var science_grade = sheet.getRange("C2").getValue();

var start_row = 6;
var last_row = sheet.getLastRow();

for(var i = start_row; i var math_point = sheet.getRange("B" + i).getValue();
var science_point = sheet.getRange("C" + i).getValue();
if(math_point >= math_grade) {
var math_range = sheet.getRange("D" + i);
math_range.setValue("合格");
}
if(science_point >= science_grade) {
var science_range = sheet.getRange("E" + i);
science_range.setValue("合格");
}
}
}

B先生
スプレッドシート

プログラム

function judge() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();

var kijun = 80;

var last_row = sheet.getLastRow();

for(var i = 3; i var lang_point = sheet.getRange("B" + i).getValue();
var social_point = sheet.getRange("D" + i).getValue();
if(lang_point >= kijun) {
sheet.getRange("C" + i).setValue("合格");
}
if(social_point >= kijun) {
sheet.getRange("E" + i).setValue("合格");
}
}
}

さて、2人の先生のスプレッドシートとGASを比べてみます。

共通点 評価・判定の方法評価・判定の出力方法
相違点 スプレッドシートのフォーマット、評価・判定の出力場所A先生は算数と理科で基準点が異なるが、B先生は基準点が同じ基準点のプログラム内での名前

相違点は各々のシートに残し、共通点はライブラリ化したいところですね。

ライブラリ化の前に、まずは共通点をうまく関数化してみましょう。今回は、以下のように点数を読み込み、基準を超えていれば”合格”と書き込む処理を、関数として共通化し切り出します。

引数 基準点、点数を読み込むセル、合格と書き出すセル
処理 点数を取得点数と基準点を比較し、基準点以上の場合、対象のセルに”合格”を出力戻り値はなし

プログラム

function setResult(grade, point_range, output_range) {
var point = point_range.getValue();
if (point >= grade) {
output_range.setValue("合格");
}
}

setResult()という共通関数にうまく切り出せました。このsetResult()をライブラリ化していく手順を説明します。

1. GASファイルを新規作成

Googleドライブから、「作成」→「もっと見る」→「GAS」→「空のプロジェクト」で新しいGASを作成します。

2. ライブラリ化する関数を記述し保存する

作成したGAS内に共通関数を実装します。今回はsetResult()関数です。実装したら保存します。「ファイル」→「保存」を選択しましょう。すると、デフォルトで「無題のプロジェクト」という名前になっていますので、適切な名前に書き換えます。ここでは、「成績表ライブラリ」としておきます。

3. GASファイルをバージョン管理する

GASをライブラリ化するには、バージョン管理下に置かなければなりません。「ファイル」→「バージョンを管理」を選択すると以下の様な画面が表示されます。

入力ボックスにはバージョンに関するコメントを入力できます。ここでは「初期バージョン」と入力します。入力後、「新しいバージョンを保存」を押します。

バージョン1として登録されました。

4. ライブラリを利用するGASから、ライブラリ利用登録をする

ライブラリ利用登録をするには、ライブラリのプロジェクトキーが必要です。プロジェクトキーを参照するには、ライブラリとするGASから「ファイル」→「プロジェクトのプロパティ」を開きます。

ここに表示されている、プロジェクトキーをコピーしておいて下さい。後程ペーストします。

5. ライブラリ利用側のGASに、使用するライブラリを登録する。

次に利用側のGASで操作します。国語、算数を評価するGASで「リソース」→「ライブラリを管理」を選択します。

「ライブラリを検索」の欄に、先ほどメモしておいたプロジェクトキーを入力し、選択を押します。すると、「成績表ライブラリ」が表示されます。

先ほど登録したバージョンである1を選択し、識別子をつけましょう。識別子はプログラム中で使用する事になるライブラリの名前です。従って、ライブラリを示す分かり易い名前を付与すべきです。ここでは「Result」としておきます。

同様の操作をB先生のGASでも実施します。

6.ライブラリを使用する様にプログラムを変更

5の手順で付与した識別子である”Result”でライブラリにアクセスできる様になりましたので、プログラムを識別子でアクセスする様に修正しましょう。具体的には以下の様に呼び出す事で、ライブラリのsetResult()を実行できる様になります。

Result.setResult( );

GASのエディタ内で、Result. と入力すると、メソッドの候補があらかじめ表示され、クリックで選択できるようになり、便利です。

結果、以下の様なプログラム構成になります。

A先生のGAS
function judge() {
var sheet = SpreadsheetApp.getActiveSheet();var math_grade = sheet.getRange("C1").getValue();
var science_grade = sheet.getRange("C2").getValue();

var start_row = 6;
var last_row = sheet.getLastRow();

for(var i = start_row; i var math_point_range = sheet.getRange("B" + i);
var science_point_range = sheet.getRange("C" + i);
var math_output_range = sheet.getRange("D" + i);
var science_output_range = sheet.getRange("E" + i);
Result.setResult(math_grade, math_point_range, math_output_range);
Result.setResult(science_grade, science_point_range, science_output_range);
}
}
B先生のGAS
function judge() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();var kijun = 80;

var last_row = sheet.getLastRow();

for(var i = 3; i var lang_point_range = sheet.getRange("B" + i);
var social_point_range = sheet.getRange("D" + i);
var lang_output_range = sheet.getRange("C" + i);
var social_output_range = sheet.getRange("E" + i);
Result.setResult(kijun, lang_point_range, lang_output_range);
Result.setResult(kijun, social_point_range, social_output_range);
}
}
ライブラリプロジェクト名:Result
function setResult(grade, point_range, output_range) {
var point = point_range.getValue();
if (point >= grade) {
output_range.setValue("合格");
}
}

セクション5 プログラムを途中で中断する

プログラムを作っていると、今の記述で正しいのか、思った通りに動作するのか確認したくなります。あるいは作ったプログラムが思った通りに動作しない場合、どこに原因があるのか突き止めたくなります。この様な作業を「デバッグ」と言います。GASにはデバッグを支援するためのツールである「デバッガ」が備わっており、デバッガを使うと処理を途中で止めながら、変数を確認したりする事ができます。このセクションでは、デバッガの使い方を説明していきます。

デバッグ作業は基本的には、「処理を途中で止める」「処理をステップ実行しながら動作を確認する」の繰り返しになります。処理をステップ実行する際には、ステップイン、ステップオーバー、ステップアウトの3つの動作を駆使して進めていきます。

ステップ実行のそれぞれの振る舞いを以下で説明します。

 

名称

説明

ステップイン 命令単位で一つ一つ実行される。関数の場合は、関数内の命令単位で実行。
ステップオーバー ステップインと同じだが、関数の場合は、関数毎の実行。
ステップアウト 現在実行中の関数のすべての処理を実行し、呼び出し元に復帰します。

1. 処理の停止位置を決める

GASエディタの行数をクリックすると赤丸が表示されます。これにより止める位置が指定できます。この止める位置の事を「ブレイクポイント」と呼びます。

2. デバッガを起動し、処理を途中で止める

虫のマークのボタンを押すとデバッガが起動します。すると処理を実行してブレイクポイントで処理が止まります。画面下部には変数を確認できるビューが表示されます。

 3. ステップ実行する

3種類のステップ実行ボタンは画面上部にあります。動作させたいボタンで処理を進めます。

この状態でステップインすると、7行目の関数sum()に移行します。ステップオーバーすると4行目に移行します。ステップアウトすると、このデバッグは終わってしまいます。従ってステップアウトの使いどころは、関数sum()に移行してからでしょう。sum()の中でステップアウトすると、8行目の処理を実施した後に4行目に移行します。

セクション6 シートを入力画面として使う

スプレッドシートを入力画面にみたて、ユーザインターフェースとして使う事もできます。ユーザの入力完了のトリガは画面上部のメニューから実施できます。シートopenのトリガを拾ってメニューを構成します。そのメニューがユーザからのトリガを得て関数が動作する様にします。

1. メニューを構成する。

スプレッドシートからGASエディタを選択する際、「スプレッドシート」を選択します。

この手順で作成したGASには、メニューの構成と実行関数が定義されている雛形が記述されたGASファイルが既に完成している状態になっています。

もちろん他の手順で作られたGASでも、上記の様にonOpenとメニュー構築ロジックを記述すればメニューを構築できます。onOpen()関数を起動させるために、再度スプレッドシートを開き直してみます。

すると、メニューに”Script Center Menu”が追加されています。このメニューをクリックすると”Read Data”というサブメニューが表示されます。あとは、メニューの名称、実行する関数等を変更し、お望みのメニューに仕上げましょう。

セクション7 メッセージボックスとインプットボックス

プログラムを作っていると、人間の判断が必要なケースが生じる事があります。例えば予想外なことが発生したとき、処理を続けるかどうかの判断や、処理の途中で必要になった人間の判断が伴う入力データといったものです。その様な時には、メッセージボックスやインプットボックスを使用します。

メッセージボックスの使い方

メッセージボックスとは、処理中にメッセージを表示したい場合や、人間に意思確認をしたい場合に使用します。単にメッセージを表示したい場合には次の様にします。

 書式 Browser.msgBox( “hello!” );

すると、次の様なメッセージボックスが表示されます。

また、引数のバリエーションによって、様々なメッセージボックスを表示する事ができます。GASで用意されている、メッセージボックスの表示パターンを以下にまとめます。

パターン説明

 書式

説明

Browser.msgBox(prompt) メッセージを指定して、OKボタンのみ存在するメッセージボックスを表示します
Browser.msgBox(prompt, buttons) メッセージとボタンの種類を指定して、メッセージボックスを表示します。
Browser.msgBox(title,prompt,buttons) メッセージ、ボタン、タイトルを指定して、メッセージボックスを表示します。

引数説明

 

名前

タイプ

説明

prompt String 表示するメッセージです。文字列を指定します。例) “hello!”
buttons Buttons GoogleAppsScriptが用意しているButtonsというオブジェクトの常数値を指定します。現在では次の種類のボタン種が用意されています。OKOK_CANCELYES_NOYES_NO_CANCEL例)Browser.msgBox(“Greetings”, “hello world”, Browser.Buttons.YES_NO);
title String 表示するタイトルです。文字列を指定します。例)”重要なお知らせ”

復帰値説明

ユーザが入力したボタンの種類が文字列で返却されます。

例)以下のプログラムを実行してみます。

var result = Browser.msgBox(
"質問", "いずれかのボタンを押下せよ", Browser.Buttons.YES_NO_CANCEL );
Browser.msgBox("押下したボタンは、" + result + "です");

まずメッセージボックスが表示されます。

「はい」を押下します。次に表示されるメッセージボックスは、以下の通りです。

「yes」という文字列が返却されている事が分かります。同様に、「いいえ」では「no」が、「キャンセル」では「cancel」が返却されます。

インプットボックスの使い方

インプットボックスとは、メッセージボックスと同様ですが、さらに文字列の入力を人間に求める場合に利用します。次の様にします。

var name = Browser.inputBox("名前を入れて下さい” );

すると、次の様なメッセージボックスが表示されます。

また、引数のバリエーションによって、様々なインプットボックスを表示する事ができます。GoogleAppsScriptで用意されているインプットボックスの表示パターンを以下にまとめます。

パターン説明

 書式

説明

Browser.inputBox(prompt) メッセージを指定して、OKボタンのみ存在するメッセージボックスを表示します
Browser.inputBox(prompt, buttons) メッセージとボタンの種類を指定して、メッセージボックスを表示します。
Browser.inputBox(title,prompt,buttons) メッセージ、ボタン、タイトルを指定して、メッセージボックスを表示します。

引数説明

メッセージボックスと同じです。

復帰値説明

ユーザが入力した文字列か、cancelを押した場合のみ”cancel”が返却されます。
例)以下のプログラムを実行してみます。

var result = Browser.inputBox("質問", "名前を入力し、いずれかのボタンを押下せよ",
Browser.Buttons.YES_NO_CANCEL);
Browser.msgBox("結果は、" + result + "です");

すると、次の様なインプットボックスが表示されます。

入力された文字列が結果として返却されます。

もし「キャンセル」を押下したとすると・・・

結果は「cancel」という文字列になります。

TIPS:
お気づきの方もいらっしゃるかもしれません。インプットボックスの場合「いいえ」のボタンを押されても判断できないのです。従って、ボタンを構築する際は、OKかOK_CANCELのいずれかで構築する様にした方がよいでしょう。実用上それで問題は無いと考えられます。

今回は以上となります。次回は、これまでに学んだことをベースに、実際にプライベートや仕事で役立つ例題を学んでいきます。

本連載記事のアーカイブは以下からご覧頂けますので、是非連載されている他の記事も合わせてご覧頂ければと思います。

株式会社らしくのインターン応募はこちら

オススメ記事一覧

もっと見る
完全無料!

1で登録完了!

エンジニアの仕事・年収や選考ノウハウ記事が読めるほか、
会員にはプログラミング講習やES・面接対策などリアルな無料サポートも充実。
ここだけの求人情報も多数。

今すぐ新規会員登録
Page Top