#133 シートを生徒数分コピーする(プログラムの解説)
今回の記事は、↓ の記事(#132)で ChatGPT に作成してもらったプログラムについて、45行くらいの身近なプログラムですが、どのようなことをしているプログラムなのかを簡単に解説を行ってみます。
空行を含めて 45行と比較的短いプログラムなのですが、全体を一画面に表示できなかったので、冒頭のファイル全体のコメント部分が少し欠けていますが、プログラムの全体像は以下のような感じです。
1) 関数の宣言とエラー処理
実際のプログラムは、12行目の関数の宣言からはじまっています。この上の部分は、諸々のコメントが記述されており、プログラムの動作には直接関係ありません。
ChatGPT が作成してくれた関数は copySheets というもので、この名前でスプレッドシートから呼び出すことで、処理が実行されます。
13行目の try と 41行目の catch は、エラー処理のために組み合わせて使うものです。13行目の { と 41行目の } がプログラムのブロックとなりますが、このブロックの中で、予期しないエラー(どうにもならないエラー)が発生した場合には、41行目からの catch のブロックが実行されるようになっています。
今回は、42行目のように Logger.log でエラーの発生した状況(エラーメッセージ)をログに記録しています。
Logger.log の引数として指定されている文字列は以下の記事(#24)でも説明しているテンプレートリテラルを用いた記述で、文字列中の ${ } の中は変数の内容に置き換えられます。逆引用符(バッククォート)と単一引用符(シングルクォーテーション)は 見分けにくいので注意しましょう。
この try と catch については、↓ の URL で更に詳しく説明されているので、こちらも確認してみてください。 とは言え、予期しないエラーが発生してしまうと、ほとんどの場合には処理が継続できないでしょうから、多くの場合はエラーが発生した状況を記録するためにしか使えないと思います。
2) バインドされたスプレッドシートを取得
関数のはじめ、15行目で行っているのは処理を行うために必要な情報が入力されているシート(配布しているスプレッドシートの場合、シート「シートの複製」)を取得しています。
この 15行目には、以下の 3つの内容の処理が組み合わさっています。
Google スプレッドシートを扱うためのクラスである SpreadsheetApp のメソッド getActiveSpreadsheet によって、このプログラムが呼び出されたスプレッドシートを取得する。
前項で得られたスプレッドシート(Class Spreadsheet)の getActiveSheet によって、このプログラムが実行されたシートを取得する。
前項で得られたシート(Class Sheet)を、宣言した変数 settingsSheet に格納する。 ※この変数は const で宣言されているので、これ以降のプログラム中では変更できない。
この部分の処理を 1. と 2. を分けると、↓ のような感じになります。処理としては同じ内容ですが、この例では ss に格納しているスプレッドシートのクラスを、これ以降のプログラムで使うのであれば、このように別の変数にするといいでしょう。
これ以降でスプレッドシートのクラスを使わないのであれば、今回のプログラムのように 1行にまとめて記述してもいいと思います。
変数宣言の const については、以前の ↓ の記事(#20)でも触れていますが、変数というよりは定数として宣言しているような感じです。
3) 「ファイルID」を取得
18行目では、前項で得られた必要な情報が入力されているシート(配布しているスプレッドシートの場合、シート「シートの複製」)のセル A2 から入力されている内容を取得しています。
取得した内容は、変数 targetFileId に格納しています。この変数も、これ以降で変更されることがないため、var や let ではなく、const で宣言されています。
セルの範囲を指定するための getRange には、いくつかの種類があり、それぞれによって指定する引数が異なります。今回のプログラムでは、A1 表記または R1C1 表記で指定する getRange(a1Notation) を用いているので、引数には ’A2’ と直接記述されています。
getRange に続けて getValue を行うことで、指定された範囲(今回の場合は、単一のセル)の内容を取得できます。
19 ~ 21行目では、変数 targetFileId に格納された文字列が、空であるかをチェックしています。!targetFileId と 変数名に ! を追加することで、空であれば true、空でなければ false となり、if で判定しています。
20行目が実行されるときは、変数 targetFileId が空だったということになります。その場合、これ以降の処理を継続しようがないので、throw によって例外エラーを発生させています。
throw new Error('A2 セルにファイルIDが入力されていません。');
と throw に続けて記述していることで、このメッセージが前述の 42行目のエラー処理でログに記録されます。
4) 処理対象スプレッドシートの取得
24行目では、前項で読み込んだ targetFileId を用いて、コピー元となるスプレッドシートを開きます。Google スプレッドシートを処理するためのクラス(Class SpreadsheetApp)の openById(id) に、targetFileId を引数として渡すことで処理しています。
ここで、targetFileId にファイル ID として適切ではない内容が指定されていた場合には、例外エラーが発生し、下図のようにログが記録されます。
変数 targetFileId によってスプレッドシートが開けたら、変数 targetSpreadsheet にスプレッドシートを処理するためのクラス(Class Spreadsheet)を格納します。 ※この変数も、これ以降のプログラムで変更しないため、const で宣言されています。
25行目では、スプレッドシートを処理するためのクラス(Class Spreadsheet)の getSheets() によって、当該スプレッドシート内にあるシート(Class Sheet)の一覧を、配列として取得します。
そして、先頭のシートを取得するために [0] と指定することで、変数 sourceSheet には先頭のシートのクラスが格納されます。 ※この変数も、これ以降のプログラムで変更しないため、const で宣言されています。
5) シート名を取得
28行目では、セル C2 以降に設定されている複製するシートに設定する名前を取得しています。
これまでのプログラムと同様に、いくつものメソッドが連結された形になっていますが、下図のように書き換えると、3つの処理が連結されていることがわかります。
このようにプログラムを分割した状態で、列 C に以下のように入力されていた状態で、プログラムが実行されると、
それぞれの変数(分割した 28 ~ 30行目)には、以下のような内容が記録されます。
分割した 28行目:values
getRange で範囲を指定し、getValues でセルの内容を取得したもの。二次元の配列に値が格納された形になっている。
今回の場合、values[0][0]、values[1][0]、values[2][0]、values[3][0]、values[4][0]、values[5][0]、values[6][0]、… という感じで、n行・1列の二次元配列になります。
分割した 29行目:flatValue
JavaScript の配列に対して利用できるメソッド flat を用いて、二次元配列を一次元配列に変換します。
この結果、n個の要素を持つ一次元配列になります。
分割した 30行目:sheetNames
この段階では、配列の要素には空欄が含まれている場合があります。 ※例の場合は C5 の空欄部分や、C7 以降の何も入力されていない部分は、空欄となります。
この空欄を、処理対象から除くために、配列に対して利用できる filter メソッドを利用します。
filter メソッドの
const sheetNames = flatValue.filter(name => name);
と引数に指定されている name => name という部分は、以下 URL で説明されているコールバック関数としてのアロー関数です。実際に実行されるイメージは、次のような感じです。
=> の前に書かれている name によって、配列 flatValue の要素が一つずつ name という引数でコールバック関数に渡されます。 ※シート名が格納されている配列なので name となっているが、この変数名は何でもいい。
=> の後ろに書かれている name は、このコールバック関数がどのような判断をするかの条件式となる。引数として与えられた name だけが条件式として指定されているので、空欄であれば false、空欄でなければ true を戻すことになる。
filter メソッドは、配列の要素それぞれに対して、与えられたコールバック関数を実行し、条件を満たす要素(true の要素)だけを新しい配列として返す。
結果として、変数 sheetNames は空欄を除いた、シート名が格納された配列となります。
ここまでの処理で、変数 sheetNames には空欄を除いた、シート名が格納された配列となります。 以下のように length メソッドを使うことで、配列の要素数を確認できます。
if (sheetNames.length === 0) {
要素数が 0 であれば、これ以降の処理を継続しようがないので、throw によって例外エラーを発生させています。
この判断をしているところで、== ではなく === が使われていますが、これは厳密等価と呼ばれる演算子で、型なども含めて等しいことを確認するものです。
6) シートのコピー処理
この部分が、このプログラムの目的となる処理を行っているところです。
ここまでに用意した配列 sourceSheet の内容から、シートをコピーします。
34行目の forEach によって、配列 sheetNames の要素が一つずつ、アロー関数を実行します。アロー関数では、name という変数で { } 内のブロックを実行します。 ※ブロック内の処理というのは、今回の場合、35行目を指す。
35行目では、次の 2つの処理を行っています。
コピー元のシートである sourceSheet を、copyTo(spreadsheet) によって複製する。 ※このメソッドの結果(戻り値)は、コピーされたシート(Class Sheet)
コピーされたシートの名前を setName(name) によって変更する。
既に指定したシート名が存在していた場合には、例外エラーが発生し、下図のようにログが記録されます。 ※上記の 2. の時点で、例外エラーが発生
その結果、「~ のコピー」というシートが残った状態で終了することになります。
7) スプレッドシートの表示更新
38行目では、念のためにだと思いますが、スプレッドシートの表示更新(flush メソッド)を行っています。
このプログラムの目的としている処理の場合、実際に処理対象となっているスプレッドシートは、プログラムを実行しているスプレッドシートとは別のスプレッドシートなので、この flush メソッドにどこまでの効果があるのかはよくわかりません。
39行目は、処理結果に影響する部分ではありませんが、プログラムの処理が終了したことをログに記録するものです。
わたし自身が作ったら…
今回のプログラムは、ChatGPT が作成してくれたものですが、同じプログラムをわたし自身が作成したら、実行結果は同じでも、もう少し違ったものになったと思います。
違うであろう部分は、以下のような感じです。
forEach や filter といった、今風の JavaScript らしい記述方法は使わない。
わたしが C 言語から本格的にプログラミングをはじめた古いタイプだからかもしれませんが、あまり新しい記述方法を使おうとしない傾向があるな、と自身でも感じています。
新しい記述方法ではなく、古い記述方法を用いることで、より多くの人に理解しやすいプログラムになるのではないか、という効果もあるんじゃないかと思います。 と言うか、思いたい。😅
プログラムの実行結果を Logger.log だけでなく画面に表示
現状では、try ~ catch で例外エラーが発生した時の対応をしているものの、その結果を Logger.log でログに記録しているだけなので、プログラムを実行していると、スクリプトエディタを開いて、「実行数」のタブからログを確認しなければ、その結果がわからない。
UI クラスや、以下の記事(#21)で触れたように toast も使って、プログラムを実行している人がわかるようにしたらいいんじゃないか?
コピーを作成する前に、複製しようとするシート名のシートが既に存在しているかどうかをチェックすることは、そういった処理を追加すれば可能ですが、今回のプログラムの主旨からはそこまで必要ないように考えられるので、わたしが手作りしても行わないと思います。
最後に
今回のプログラムは、冒頭でも説明したように、ChatGPT に作成してもらったものです。最近の JavaScript らしい記述で作成されているため、forEach や filter、アロー関数など難しい部分があったかもしれません。そういった部分が、この記事で少しは払しょくできるといいな、と思います。
わたし自身にしてみると、このような「スクリプトを作ること」が目的になっているような感じですが、このスクリプトが何かの役に立てば幸いです。
これらの記事への感謝の意は、記事に対する「スキ ♡」や、以下のような Amazon のアフィリエイトリンク経由での商品の購入、note の「チップ」機能を利用してもらえると、このようなプログラム作成の励みになります。😍
Amazon のアフィリエイトリンクを開くと、それ以降 24時間以内に商品を購入した場合に、紹介者(この場合は、わたし)に紹介料が発生します。