提出用フォーマット整形を1クリックで — #REF!エラーだらけを防ぐ
実際のビジネスシーンで使える自動化100例をGASで実装するシリーズ「GAS自働化100本ノック」。その11本目
提出用フォーマット整形を手作業でやると
月末、上司や取引先に渡す月次の売上シートを準備するとき、元のシートには「原価」「担当者社員ID」「担当者メアド」など、外には出したくない列が混ざっている。
さらに金額列は `=数量*単価` のような数式で、別シートを参照していたりもする — このまま渡すと相手側で `#REF!` だらけになりかねない。
そこで、提出用のフォーマット整形を手作業でやるとなると…
-
「名前を付けて保存」で「売上_5月_提出用シート」を作る
-
提出用で 原価、担当者社員ID、担当者メアド 列を削除
-
補助シートや参照元シートを確認
-
金額 列をコピーして値貼り付け
-
必要ならシート全体を値貼り付け
-
保存して、開き直してチェック…
あー、めんどー
こういう手間こそ、GASでさっさと片付けましょう!
GASでできること
-
対象シートと除外したい列を「提出設定」シートで管理
-
元シートをコピーして「提出用_2026-05-10」のような日付つきシートを作成
-
指定した除外列をすべて削除
-
セルの数式をすべて値に固定
GASなら手作業の工程を1クリックで自動化します!
そのコードがこちら
コード.gs
// コード.gs
/**
* 「提出設定」シートで指定した対象シートから、不要列を削除した「提出用_YYYY-MM-DD_HH-mm」シートを切り出す。
* - 元シートには触れず、コピー後のシートだけに対して列削除+数式→値固定を行う
* - 何度実行しても新しいタイムスタンプ付きシートが追加される(過去の提出版はそのまま残る)
* メニュー「自動ボタン → 提出用シートを切り出す」から実行。
*/
const SUBMIT_CONFIG_SHEET = '提出設定';
const SUBMIT_PREFIX = '提出用_';
function makeSubmitSheet() {
const ui = SpreadsheetApp.getUi();
const ss = SpreadsheetApp.getActiveSpreadsheet();
const cfg = ss.getSheetByName(SUBMIT_CONFIG_SHEET);
if (!cfg) {
ui.alert('「' + SUBMIT_CONFIG_SHEET + '」シートが見つかりません。');
return;
}
// 提出設定の読み込み(A列キー / B列値)
const last = cfg.getLastRow();
let targetName = '';
const excludeCols = [];
if (last >= 2) {
const cfgData = cfg.getRange(2, 1, last - 1, 2).getValues();
for (const [key, value] of cfgData) {
const k = String(key).trim();
const v = String(value).trim();
if (!k || !v) continue;
if (k === '対象シート') targetName = v;
else if (k === '除外列') {
// カンマ「,」または読点「、」区切りで複数指定可(行を分けて書いてもOK)
const parts = v.split(/[,、]/).map(function(p) { return p.trim(); }).filter(function(p) { return p; });
for (const p of parts) excludeCols.push(p);
}
}
}
if (!targetName) {
ui.alert('「' + SUBMIT_CONFIG_SHEET + '」のA列に「対象シート」、B列にシート名を入れてください。');
return;
}
const target = ss.getSheetByName(targetName);
if (!target) {
ui.alert('対象シート「' + targetName + '」が見つかりません。');
return;
}
// ── ① 対象シートをコピー(提出用_日付) ──
const ts = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM-dd');
let newName = SUBMIT_PREFIX + ts;
// 同日中に再実行された場合は _2, _3, ... を付けて衝突回避
if (ss.getSheetByName(newName)) {
let n = 2;
while (ss.getSheetByName(newName + '_' + n)) n++;
newName = newName + '_' + n;
}
const submit = target.copyTo(ss).setName(newName);
// 末尾タブに移動してアクティブに
ss.setActiveSheet(submit);
ss.moveActiveSheet(ss.getNumSheets());
// ── ② 除外列を「右から」削除 ──
// 左から削除すると残りの列番号がズレるので、必ず右からまとめて削除する
const lastCol = submit.getLastColumn();
const headers = submit.getRange(1, 1, 1, lastCol).getValues()[0]
.map(function(h) { return String(h).trim(); });
const removeColNumbers = [];
for (let c = 0; c < headers.length; c++) {
if (excludeCols.indexOf(headers[c]) >= 0) removeColNumbers.push(c + 1);
}
removeColNumbers.sort(function(a, b) { return b - a; });
for (const c of removeColNumbers) submit.deleteColumn(c);
// ── ③ 残った範囲の数式を値に固定 ──
// getValues() は数式を「実行結果の値」で返すので、そのまま setValues() で書き戻すと
// 数式が消えて値だけが残る(手作業の「値貼り付け」と同じ効果)
const lastRow = submit.getLastRow();
const remainingCol = submit.getLastColumn();
if (lastRow >= 1 && remainingCol >= 1) {
const range = submit.getRange(1, 1, lastRow, remainingCol);
range.setValues(range.getValues());
}
SpreadsheetApp.flush();
ui.alert(
'「' + newName + '」を作成しました。\n' +
'除外列: ' + removeColNumbers.length + ' 列、残り: ' + remainingCol + ' 列\n' +
'数式は値に固定済みです。'
);
}
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('自動ボタン')
.addItem('提出用シートを切り出す', 'makeSubmitSheet')
.addToUi();
}使い方
① 新規スプレッドシートを用意
新規スプレッドシートで動作確認してから本番に導入するのがおすすめです。
※ 既存スプレッドシートに導入する場合:元シートには触れませんが、コピー版で試すと安全です。
② 「提出設定」シートを作成
シートを1枚追加して名前を「提出設定」にし、A列にキー、B列に値を入れます。
-
対象シート — 切り出し元のシート名(1つだけ)
-
除外列 — 削除したい列名。複数指定したい場合は `原価, 担当者社員ID` のように カンマ(`,` または `、`)区切り で記入
③ 拡張機能 → Apps Script にコード.gsを貼り付けて保存
④ スプレッドシートをリロード すると、メニューに「自動ボタン」が現れます。初めて実行したときだけ承認ダイアログが表示されます(承認手順は第1回と同じ)。
⑤ メニュー「自動ボタン → 提出用シートを切り出す」を実行
実行直後、シートタブの末尾に「提出用_YYYY-MM-DD」シートが追加されます。
-
「提出設定」で指定した除外列が削除済み
-
数式(金額列の `=D2*E2` など)は値に固定済み
-
元シート(売上_5月)は何も変わらない
⑥ 別の月、または同じ月にもう一度提出する場合は、もう一度メニューを実行する
別の日に実行すれば新しい日付のシートが、同じ日に再実行した場合は `_2`, `_3` のような連番付きシートが追加されます。
ワンポイント・レッスン
`createMenu().addItem().addToUi()` — シートのメニューをカスタマイズする3点セット
第1回からずっとコードの末尾にいる、この `onOpen` の中身。
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('自動ボタン')
.addItem('提出用シートを切り出す', 'makeSubmitSheet')
.addToUi();
}これがあるから、シートを開くたびに「自動ボタン」という独自メニューが現れて、そこから関数を実行できます。
メニュー1個追加が4行で済む のがGASの強みなので、ここで仕組みを分解しておきます。
① メニュー作成は「3つのメソッドを順に呼ぶ」だけ
`SpreadsheetApp.getUi()` で取得した UI オブジェクトに対して、3つのメソッドを メソッドチェーン(`.` で連結)で呼びます。それぞれが「次に何を返すか」が決まっていて、流れるように書けます。
-
`createMenu('自動ボタン’)` — メニュー名(タブの並びに表示される文字列)を渡して、メニューオブジェクトを作る
-
`addItem('提出用シートを切り出す’, 'makeSubmitSheet’)` — 表示名と「実行する関数名(文字列)」のペアでメニュー項目を1つ足す。
-
`addToUi()` — 作ったメニューを実際にUIに登録する。これを呼ばないとメニューは表示されない
② `onOpen` はメニューを「いつ生成させるか」のフック
`createMenu…addToUi()` をどこで呼べばいいか——その答えが `onOpen` です。`onOpen` という名前の関数をスクリプトに置いておくと、シートを開いた瞬間にGASが自動で呼んでくれる 仕組み(GASの「シンプルトリガー」と呼ばれるグループ)。
第9回で扱った `onEdit(e)` と同じ仲間で、`on○○` という予約名は他にもいくつかあります。
つまり「シートが開かれる → `onOpen` が呼ばれる → その中でメニューを組み立てる → ユーザーがメニューを使える」という時系列です。
③ 区切り線・複数項目を足したい時は同じ要領で続ける
メニュー項目が増えたら、`addItem()` を続けて呼ぶだけです。
区切り線を入れたければ `addSeparator()` を間に挟みます。
実際、本記事の有料エリア(拡張コード.gs)の `onOpen` はこの形になっています。
SpreadsheetApp.getUi()
.createMenu('自動ボタン')
.addItem('提出用シートを切り出す', 'makeSubmitSheet')
.addSeparator()
.addItem('提出用シートを切り出す(マスク付き)', 'makeSubmitSheetWithMask')
.addItem('提出履歴をクリア', 'clearSubmitLog')
.addToUi();`createMenu()` で始め、`addItem()` や `addSeparator()` を好きなだけ並べ、最後に `addToUi()` で締める——この基本形さえ覚えれば、無料機能だけのスクリプトでも、機能が増えた拡張版でも、同じパターンで書けます。
カスタムメニューはGAS自動化の「玄関」です。
読者にとっては「Apps Scriptエディタを開かなくても、シートのメニューから自分のスクリプトが呼べる」という体験を提供する、いちばんユーザーに近い部分。
メニュー名は分かりやすく、項目名は何が起きるか想像できる動詞で — この2点を意識して書くと、自分以外の人にも渡しやすい自動化になります。
まとめ
-
「対象シート」と「除外列」を1枚の設定シートに書いておけば、ボタン1つで「提出用_日付」シートを切り出します。
-
元シートは触らず、不要列を消し、数式も値に固定済み。
-
月末の手間作業が1クリックになり、何度実行しても原本は無傷のまま提出版だけが増えていきます。
でも、ちょっと待って!
シートに非表示列やメモがあって、そこに外部に出したくない情報があったらどうなるの?!
確かに。
コード.gsでは、見えていない情報(非表示列・非表示行・メモ・フィルタ)までカバーできていなかった。
それらも一つ一つ手で潰すとなると面倒です。
拡張コードで自動化しましょう!
拡張コードでできること
-
自動クリーンアップ — 提出シートから 非表示列・非表示行 を削除、メモを一括クリア、フィルタを解除。
この記事の続きはnoteで公開しています。
