GAS実践パターン
GASで繰り返し使える「型」を10パターンに整理。トリガーの単純/インストーラブルの違い、定期集計メール、差込送信、onEdit自動記入、getValues/setValues一括処理、UrlFetchApp外部連携、設定保存と例外処理まで実務コード付きで解説します。
GASの実務は、ゼロから書くものではなく「型」を組み合わせるものです。 第10章 GAS入門 で関数の書き方と実行を覚えたら、次は現場で繰り返し登場するパターンを手札にします。本章は、そのまま貼って動く再利用可能な「型」を集めた実践集です。各パターンを「目的→コード→ポイント/注意」の形でまとめました。
本章で扱うパターンは次の10です。
- トリガー総論 — 単純トリガー vs インストーラブルトリガー
- 時間主導の定期実行 — 毎朝の集計メール
- メール自動送信 — シートの行をループして差込送信
- onEdit — 編集に応じた自動記入・着色
- データ処理の効率化 —
getValues/setValuesの一括処理 - 外部API連携 —
UrlFetchApp.fetchと Webhook 通知 - カスタムメニュー — シート上に実行ボタンを置く
- 設定の保存 —
PropertiesServiceで機密を直書きしない - 例外処理 —
try/catchとログ、失敗通知 - クォータ・実行時間の制限 — 上限と回避の考え方
コードは Apps Script(JavaScript)です。構文に迷ったら一次情報の公式リファレンスを確認してください。
1. トリガー総論
GASを「自動で動かす」仕組みがトリガーです。大きく2種類あり、できることが違います。
単純トリガー
特定の名前の関数(onOpen / onEdit / onSelectionChange など)を定義しておくと、対応する操作で自動的に呼ばれます。設定作業は不要ですが、外部サービスへのアクセスができない、認可を必要とする操作はできない、といった制約があります。なお フォーム送信(onFormSubmit)や時間主導は単純トリガーには存在せず、下記のインストーラブルトリガーで登録します(関数名を onFormSubmit にしただけでは自動実行されません)。
インストーラブルトリガー
ScriptApp.newTrigger(...) で明示的に登録するトリガーです。時間主導(毎朝7時など)やイベント駆動を設定でき、メール送信や外部API呼び出しなど認可の要る操作も実行できます。
| 観点 | 単純トリガー | インストーラブルトリガー |
|---|---|---|
| 設定方法 | 決まった名前の関数を書くだけ | ScriptApp.newTrigger で登録 |
| 起動契機 | 開く | 編集 | 選択変更 | 時間主導 | 編集 | 開く | フォーム送信 など |
| 外部サービス呼出 | 不可 | 可 |
| メール送信 | 不可 | 可 |
| 時間主導の定期実行 | 不可 | 可 |
ScriptApp.newTrigger("dailyReport").timeBased().atHour(7).everyDays(1).create();
単純トリガー onEdit の中でメール送信(外部サービス扱いで失敗する)
トリガーの種類と挙動は変更されることがあります。最新の仕様は トリガーの公式ガイド を確認してください。
2. 時間主導の定期実行
目的:毎朝、シートを集計してその結果を自分(やチーム)にメールで送る。
function dailyReport() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("売上ログ");
const values = sheet.getDataRange().getValues(); // ヘッダ含む全行を一括取得
let total = 0;
for (let i = 1; i < values.length; i++) { // 1行目はヘッダなのでスキップ
total += Number(values[i][2]) || 0; // C列(金額)を合計
}
const body = "本日時点の売上合計は " + total.toLocaleString() + " 円です。";
MailApp.sendEmail("you@example.com", "売上日報", body);
}
このトリガーは一度だけ登録します(毎回登録すると重複します)。
function setupDailyTrigger() {
ScriptApp.newTrigger("dailyReport")
.timeBased()
.atHour(7) // 7時台
.everyDays(1) // 毎日
.create();
}
3. メール自動送信
目的:シートの各行(宛先・氏名など)を読み、一人ずつ内容を差し込んでメールを送る。
function sendBulkMail() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("送信リスト");
const rows = sheet.getDataRange().getValues();
for (let i = 1; i < rows.length; i++) {
const name = rows[i][0]; // A列: 氏名
const email = rows[i][1]; // B列: メールアドレス
if (!email) continue; // 空行はスキップ
const subject = "【お知らせ】6月の予定について";
const body = name + " 様\n\nいつもお世話になっております。…";
MailApp.sendEmail(email, subject, body);
}
}
MailApp.sendEmail(宛先, 件名, 本文) が基本形です。HTMLメールやCC・添付が要るなら、オプションをオブジェクトで渡す形(MailApp.sendEmail({ to, subject, htmlBody, ... }))も使えます。
4. onEdit
目的:セルが編集されたら、隣の列に編集日時を自動で記入する。
function onEdit(e) {
const range = e.range; // 編集されたセル
const sheet = range.getSheet();
if (sheet.getName() !== "タスク") return; // 対象シートを限定
if (range.getColumn() !== 4) return; // D列(状態)の編集だけ反応
const row = range.getRow();
if (row === 1) return; // ヘッダは除外
sheet.getRange(row, 5).setValue(new Date()); // E列に更新日時を記入
}
onEdit(e) は単純トリガーで、引数 e に編集イベントの情報(e.range など)が入ります。条件付き書式で十分な着色は数式側に任せ、GASは「値の書き込み」のような数式では難しい処理に使うと役割分担がきれいです。
5. データ処理の効率化
目的:全行に税込価格を書き込む。ここが本章で最重要のパターンです。
セルを1つずつ getValue / setValue で読み書きすると、1回ごとにシートとの通信が発生し、行数が増えるほど劇的に遅くなります。範囲をまとめて getValues で配列に取り、JavaScript側で計算し、setValues で一括書き戻す のが鉄則です。
for (let r = 2; r <= 1001; r++) { const p = sheet.getRange(r,1).getValue(); sheet.getRange(r,2).setValue(p * 1.1); }一括取得→配列処理→一括書込(速い)
下のコード参照
function fillTaxIncluded() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("商品");
const last = sheet.getLastRow();
const prices = sheet.getRange(2, 1, last - 1, 1).getValues(); // A列を一括取得(2次元配列)
const result = prices.map(row => [Math.round(row[0] * 1.1)]); // 配列として一括計算
sheet.getRange(2, 2, result.length, 1).setValues(result); // B列へ一括書込
}
getValues / setValues が扱うのは常に 2次元配列(行の配列、各行は列の配列)です。1列でも row[0] のように内側の添字が要る点に注意してください。
6. 外部API連携
目的:外部のWeb APIを叩いてJSONを受け取る/Webhookに通知を送る。
function fetchJson() {
const res = UrlFetchApp.fetch("https://api.example.com/status");
const data = JSON.parse(res.getContentText()); // 本文をJSONとして解釈
Logger.log(data.message);
}
POSTやヘッダ付きの送信はオプションで指定します。次は Slack 等の Webhook へ通知する最小例です。
function notifyWebhook(text) {
const url = "https://hooks.example.com/xxxx"; // Webhook URL
const options = {
method: "post",
contentType: "application/json",
payload: JSON.stringify({ text: text })
};
UrlFetchApp.fetch(url, options);
}
UrlFetchApp.fetch(url, options) の options で method・contentType・headers・payload を渡せます。レスポンスは getContentText() で本文、getResponseCode() でHTTPステータスを取得できます。詳細は UrlFetchApp リファレンス を参照してください。外部呼び出しは認可が要るため、単純トリガーからは実行できません(パターン1・4)。
7. カスタムメニュー
目的:スプレッドシートのメニューバーに自前の項目を足し、ボタン代わりに関数を実行する。
function onOpen() {
SpreadsheetApp.getUi()
.createMenu("業務ツール")
.addItem("税込価格を一括計算", "fillTaxIncluded")
.addItem("日報を送信", "dailyReport")
.addToUi();
}
onOpen はファイルを開くたびに自動で走る単純トリガーです。addItem("表示名", "関数名") で、メニューを選ぶとその関数が手動実行されます。エディタを開かずに非エンジニアのメンバーが処理を実行できるので、配布シートの定番です。
8. 設定の保存
目的:APIキーや送信先などの設定を、コードに直書きせず安全に保存する。
function saveApiKey() {
PropertiesService.getScriptProperties().setProperty("API_KEY", "実際のキー");
}
function useApiKey() {
const key = PropertiesService.getScriptProperties().getProperty("API_KEY");
// key を UrlFetchApp のヘッダなどに使う
}
PropertiesService.getScriptProperties() はスクリプト単位の保存領域です。ユーザーごとに分けたいなら getUserProperties() もあります。設定値をコードから分離できるため、共有や引き継ぎが安全になります。
9. 例外処理
目的:処理が途中で失敗してもログに残し、必要なら自分に通知する。
function safeJob() {
try {
dailyReport(); // 本処理
} catch (err) {
Logger.log("失敗: " + err.message); // 実行ログに記録
MailApp.sendEmail("you@example.com", "GASエラー通知", String(err.stack || err.message));
}
}
時間主導トリガーは裏で動くため、失敗しても気づきにくいのが弱点です。try/catch で囲み、Logger.log でログに残しつつ、重要な処理は失敗時に自分へメールするとブラックボックス化を防げます(外部送信を伴うのでインストーラブルトリガー前提)。
10. クォータ・実行時間の制限
GASには「1回の実行時間」「1日のメール通数」「UrlFetchApp の呼び出し回数」などの上限があります。これらは変わりうるため具体値は断定しません。無料アカウントと Google Workspace で上限が異なる点も含め、必ず公式の 割り当て・制限ページ を確認してください。
回避の考え方は「一度に全部やらない」ことです。
- 一括処理で軽くする:そもそも
getValues/setValues(パターン5)で実行時間を削るのが最優先。 - バッチ分割・続きから再開:処理対象が多いときは1回あたりの件数を区切り、どこまで終えたかを
PropertiesService(パターン8)に保存します。次回はその続きから処理し、時間主導トリガー(パターン2)で小分けに繰り返します。
処理した最終行を PropertiesService に保存 → 次回はその次の行から N 件だけ処理
10万行を1回のトリガーで全部処理しようとする(実行時間の上限で途中停止する)
11. 全角・半角をそろえる正規化(カスタム関数)
全角・半角の単純な変換なら、標準関数の ASC(全角→半角)/ JIS(半角→全角)で足ります(第8章 参照)。ただし ASC は 全角→半角の一方向 で、カタカナも半角に倒します。「半角カナは全角へ、全角英数字は半角へ」と NFKC 規格の標準形に一括で寄せたい 場合は、標準関数だけでは表現しきれません。ここで GAS の出番です。JavaScript の文字列メソッド normalize("NFKC") を使えば NFKC 正規化を一発で行え、カスタム関数にすればセルから =NORMALIZE(A2) のように呼べます。
/**
* 全角の英数字・記号・半角カナを NFKC 正規化して返すカスタム関数。
* 単一セルでも範囲でも使える(範囲なら同じ形で配列を返す)。
* 使い方: =NORMALIZE(A2) または =NORMALIZE(A2:A100)
*/
function NORMALIZE(value) {
// 範囲(2次元配列)が渡された場合は各セルを変換して返す
if (Array.isArray(value)) {
return value.map(function (row) {
return row.map(function (cell) {
return cell === '' || cell === null ? '' : cell.toString().normalize('NFKC');
});
});
}
// 単一セル
return value === '' || value === null ? '' : value.toString().normalize('NFKC');
}
このカスタム関数を整形用の列に置けば、VLOOKUP / XLOOKUP のキー不一致(全角半角ゆれによる #N/A)をまとめて解消できます。=ARRAYFORMULA(NORMALIZE(A2:A)) のように範囲で一括適用するのも有効です。
パターンを支える2つの軸
正しいトリガー選び
外部送信やAPI呼び出しが要るならインストーラブル、編集やオープンへの軽い反応なら単純トリガー。この見極めを誤ると「ローカルでは動くのに自動実行で失敗する」典型的なハマりに陥ります。
一括処理の徹底
getValues/setValues の一括処理は、速度だけでなく実行時間クォータ対策でもあります。すべてのパターンの土台になる最重要の型です。
各パターンを単体で動かせるようになったら、次はそれらをつないで日々の業務フローに組み込む段階です。複数サービスの連携や、安定して動かし続けるための運用は 第12章 連携と運用 で扱います。基礎を振り返るなら 第10章 GAS入門、手作業のテンプレは 第9章 業務テンプレート実例 へ。実際に手を動かすなら 演習で鍛える、用語に迷ったら 用語集、一次情報は 参考資料 を参照してください。