Google Apps Script

スプレッドシートに関数を動的割り当て|GASで高度なカスタムメニューを作る方法

スポンサーリンク

Google スプレッドシートで、用途ごとに動的に切り替えられるカスタムメニューを実装したいと思ったことはありませんか?
本記事では、Google Apps Script(GAS)を使って、即時関数とループ処理を活用し、メニュー項目ごとに関数を動的に割り当てる方法を解説します。

通常のカスタムメニュー作成では難しい「引数付き処理」や「柔軟な関数割り当て」を行いたい方におすすめの実装テクニックです。

カスタムメニューの基本的な設置方法については、以下の記事をご参照ください。

なぜ動的なカスタムメニューが必要なのか?

もともと私が、Google ドライブ上の特定フォルダに保存した電子印鑑の画像ファイルを、スプレッドシート上で簡単に押せるようにしたいと思い、
複数の印鑑ファイル(たとえば「印鑑A」「印鑑B」など)のファイル名をもとに、「電子印鑑を押すためのメニュー」を動的に追加するという仕組みを作ろうと試行錯誤したことがきっかけでした。

しかし、Google Apps Script の標準的なカスタムメニュー作成方法では、関数名を文字列で指定する仕様になっており、メニューから呼び出される関数に任意の引数(印鑑のファイル名など)を渡すことができません

そのため、印鑑の種類が可変になる場合(ファイルを追加・削除するなど)、通常のやり方では柔軟に対応できないという課題がありました。

解決策:即時関数を活用したメニューの動的生成

今回紹介する方法では、JavaScriptの「即時関数(IIFE)」を活用し、動的にメニュー用の関数を生成しています。

即時関数(IIFE:Immediately Invoked Function Expression)とは?

定義と同時にその場で実行される関数のことです。
通常の関数は呼び出されるまで実行されませんが、即時関数は定義した瞬間に一度だけ実行され、ローカル変数やスコープを閉じ込める用途でよく使われます。
GAS でもこの構文を使うことで、グローバルに展開される関数をまとめて作成することができます。


以下のスクリプトは、Qiitaの 【GAS】スプレッドシートのカスタムメニューでアイテム毎に関数を動的に割り当てる をベースにしています。


サンプルスクリプト:動的メニューの実装

/**
 * 参考:https://qiita.com/neonemo/items/86f34ecb0db3cfc51c8d
 */
const SAMPLE = (function() {

  // 関数
  let autoFunc = {};

  // サブメニューの数だけ繰り返し
  // メニューの作成と同じ条件で繰り返す必要あり。
  for (let i = 1; i <= 5; i++) {

    // 連想配列のキーを関数名にする
    autoFunc[`sample_menu_${i}`] = function() {
      // TODO ここにメニューで呼び出された時の処理を書く
      // 今回はダイアログを表示するだけ
      Browser.msgBox(`sample_menu_${i}`);
    }
  }

  return autoFunc;

})();

/**
 * メニューの作成
 */
function setMenu() {

  // サブメニュー
  let menu = [];
  
  // サブメニューの数だけ繰り返し
  // (サンプルなので固定で5つ、作成する)
  for (let i = 1; i <= 5; i++) {
    
    // メニューの名前
    let name = `menu${i}`;
    
    // メニューでクリックされて呼び出される関数名
    let funcName = `SAMPLE.sample_menu_${i}`;

    // サブメニューのname、functionNameを配列に設定
    menu.push({
      name: name,
      functionName: funcName
    });
  }

  // メニューの作成
  SpreadsheetApp.getActiveSpreadsheet().addMenu('サンプルメニュー', menu);
}

スクリプトのポイント解説

即時関数(IIFE)を使って関数を生成

  • SAMPLE は即時関数で定義され、その中で sample_menu_1sample_menu_5 の関数を動的に作成しています。
  • これにより、事前に決まった名前の関数を1つ1つ定義しなくても済みます。

メニュー構成は同じ条件でループ

  • 関数を生成するループと、メニューを作成するループは「同じ回数」である必要があります。
  • 数がズレると、クリックしても関数が存在しないなどの不具合になります。

addMenu(name, subMenus) の使い方

  • 最後に SpreadsheetApp.getActiveSpreadsheet().addMenu() を使って、カスタムメニューをスプレッドシートに追加します。
  • このとき、第二引数 subMenus には、{name: 表示名, functionName: 関数名} の配列を渡します。

よくある質問(FAQ)

Q
関数に引数を渡す方法は本当にないのですか?
A

addMenu() で登録する関数は、あくまで「引数なしの関数名(文字列)」で指定する必要があり、直接的に引数を渡すことはできません。
引数を使いたい場合は、今回のように関数自体を動的に生成する構成が有効です。

Q
SAMPLE.sample_menu_x のような構造にしないと動きませんか?
A

GASの仕様上、addMenu で指定する関数名はグローバルスコープにある必要があります。
そのため、オブジェクト(ここでは SAMPLE)としてグローバルで定義する構成が適しています。

Q
メニュー項目の数を動的に変えるにはどうすればいい?
A

スクリプト内のループの条件(for 文)を、たとえば Drive フォルダ内のファイル数などで決定するようにすれば、メニュー数をファイルの数に合わせて動的に増減させることが可能です。

Q
即時関数(IIFE)は他の用途にも使えますか?
A

はい、変数のスコープを限定したいときや、一度だけ初期化処理をしたいときなどにも使われます。GASに限らず、JavaScript全般で活用されているテクニックです。


まとめ

本記事で紹介した方法を活用すれば、GASで作成するカスタムメニューに対して、動的に関数を割り当てることが可能になります。
即時関数とループ処理を組み合わせることで、メンテナンス性や拡張性の高いスクリプトを実現できます。

用途が複雑になりがちな業務自動化やツール化にも応用できるため、GASに慣れた方には特におすすめです。

タイトルとURLをコピーしました