受付名簿や注文一覧をスプレッドシートで管理していると、「あれ、この人2回入ってない?」と首をかしげること、ありませんか?
- 同じメールアドレスの人が、いつの間にか2行入っている
- 必須の項目が空欄のまま、あとで気づく
- 数量が「3」ではなく文字の「3」になっていて、集計が合わない
100行ほどのシートでも、目で1行ずつ確認すると10分前後かかります。しかもこの確認、毎回同じルールで見ているだけなんですよね。だったら、GAS(Google Apps Script)に任せてしまいましょう。僕のほうでサンプルのシートを使って実際に動かして確認したコードを、一緒に作っていきます。
空欄・重複・形式の間違いを、メニューから1回操作するだけで自動チェックし、問題のあるセルに色を付けて、一覧にもまとめてくれるスクリプトを作ります。
所要時間の目安は、コピペして動かすだけなら約15分です。気軽にいきましょう。
この記事を含め、この1週間は自動化・開発の実務ネタ(GAS・n8n・Python・Notion)を毎日1本ずつ公開します。ほかの記事は記事一覧からも読めます。
今回作るのは、こんなもの
メニューの「入力チェック」→「チェックを実行」を押すと、次の3つを自動で確認してくれます。
| チェックの種類 | 見つける内容 | 付く色 |
|---|---|---|
| 空欄 | 必須の列(受付日・氏名・メール)が空 | 黄色 |
| 重複 | 同じメールアドレスが2件以上ある(大文字小文字・前後の空白は無視) | オレンジ |
| 形式 | 日付として読めない、メールの形になっていない、数量が1以上の整数でない | 赤 |
さらに、問題のセルを「チェック結果」シートに一覧で書き出します。セル番号をクリックすれば、そのセルへ一気にジャンプできるので、探す手間もありません。
準備:サンプルのシートを用意する
新しいスプレッドシートを作り、シート名を「受付リスト」にして、次のダミーデータを入力します。1行目は見出しです。
| 受付日 | 氏名 | メール | 数量 |
|---|---|---|---|
| 2026/10/01 | 山田 太郎 | [email protected] | 2 |
| 2026/10/02 | 佐藤 花子 | hanako@example | 1 |
| 2026/13/40 | (空欄) | [email protected] | 0 |
| (空欄) | 鈴木 一郎 | [email protected] | 1.5 |
| 2026/10/05 | 高橋 次郎 | (空欄) | (空欄) |
テスト用に、わざと間違いを入れてあります。シートの4行目の「2026/13/40」は存在しない日付、3行目のメールは .com などが抜けた形です。
手順1:スクリプトエディタにコードを貼る
- スプレッドシートのメニュー「拡張機能」→「Apps Script」を開く
- 最初からある
function myFunction() {}を消す - 下のコードをすべて貼り付けて、保存する
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
// ===== 設定 =====
const SHEET_NAME = '受付リスト'; // チェックするシート名
const RESULT_SHEET_NAME = 'チェック結果'; // 一覧を出力するシート名(なければ自動で作る)
const HEADER_ROWS = 1; // 見出し行の数
const COL = { DATE: 0, NAME: 1, EMAIL: 2, QTY: 3 }; // 列の位置(A列=0, B列=1…)
const REQUIRED_COLS = [COL.DATE, COL.NAME, COL.EMAIL]; // 空欄NGの列
const DUPLICATE_COL = COL.EMAIL; // 重複をチェックする列
const COLORS = { blank: '#ffe599', duplicate: '#f4b183', format: '#ea9999' };
const TYPE_LABELS = { blank: '空欄', duplicate: '重複', format: '形式' };
// =================
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('入力チェック')
.addItem('チェックを実行', 'checkInput')
.addToUi();
}
function checkInput() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(SHEET_NAME);
if (!sheet) {
SpreadsheetApp.getUi().alert(`「${SHEET_NAME}」シートが見つかりません。`);
return;
}
const lastRow = sheet.getLastRow();
if (lastRow <= HEADER_ROWS) {
SpreadsheetApp.getUi().alert('チェックするデータがありません。');
return;
}
const numCols = COL.QTY + 1;
const headers = sheet.getRange(1, 1, 1, numCols).getValues()[0];
const range = sheet.getRange(HEADER_ROWS + 1, 1, lastRow - HEADER_ROWS, numCols);
const values = range.getValues();
const issues = findIssues_(values);
// 前回の色をリセットしてから、問題のあるセルだけ色を付ける(null = 色なし)
const colors = values.map(row => row.map(() => null));
issues.forEach(i => { colors[i.r][i.c] = COLORS[i.type]; });
range.setBackgrounds(colors);
writeResult_(ss, sheet, headers, issues);
ss.toast(`${issues.length}件の確認ポイントが見つかりました`, '入力チェック', 5);
}
// 問題を探す(シートには触らない部分)
// values は見出しを除いたデータ。r, c は values の中の位置(0始まり)
function findIssues_(values) {
const issues = [];
const groups = {}; // 重複チェック用:値 → 行番号の配列
values.forEach((row, r) => {
// 1) 空欄
REQUIRED_COLS.forEach(c => {
if (isBlank_(row[c])) {
issues.push({ r, c, type: 'blank', detail: '入力されていません' });
}
});
// 2) 形式(空欄でないものだけ確認)
if (!isBlank_(row[COL.DATE]) && !isValidDate_(row[COL.DATE])) {
issues.push({ r, c: COL.DATE, type: 'format', detail: `「${row[COL.DATE]}」は日付として読み取れません` });
}
if (!isBlank_(row[COL.EMAIL]) && !isValidEmail_(row[COL.EMAIL])) {
issues.push({ r, c: COL.EMAIL, type: 'format', detail: `「${row[COL.EMAIL]}」はメールアドレスの形式ではありません` });
}
if (!isBlank_(row[COL.QTY]) && !isPositiveInteger_(row[COL.QTY])) {
issues.push({ r, c: COL.QTY, type: 'format', detail: `「${row[COL.QTY]}」は1以上の整数ではありません` });
}
// 3) 重複の下準備(前後の空白と大文字小文字は無視して比べる)
if (!isBlank_(row[DUPLICATE_COL])) {
const key = String(row[DUPLICATE_COL]).trim().toLowerCase();
(groups[key] = groups[key] || []).push(r);
}
});
// 同じ値が2回以上出てきた行は、すべて重複として記録する
Object.keys(groups).forEach(key => {
const rows = groups[key];
if (rows.length >= 2) {
rows.forEach(r => {
issues.push({ r, c: DUPLICATE_COL, type: 'duplicate', detail: `同じ値が${rows.length}件あります` });
});
}
});
return issues;
}
function isBlank_(v) {
return v === '' || v === null || v === undefined || (typeof v === 'string' && v.trim() === '');
}
function isValidDate_(v) {
return Object.prototype.toString.call(v) === '[object Date]' && !isNaN(v.getTime());
}
function isValidEmail_(v) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(v).trim());
}
function isPositiveInteger_(v) {
return typeof v === 'number' && Number.isInteger(v) && v >= 1;
}
// 結果を別シートに一覧で書き出す(セル番号をクリックすると該当セルへジャンプ)
function writeResult_(ss, sheet, headers, issues) {
const sorted = issues.slice().sort((a, b) => a.r - b.r || a.c - b.c);
const gid = sheet.getSheetId();
const rows = sorted.map(i => {
const a1 = sheet.getRange(i.r + HEADER_ROWS + 1, i.c + 1).getA1Notation();
return [`=HYPERLINK("#gid=${gid}&range=${a1}","${a1}")`, headers[i.c], TYPE_LABELS[i.type], i.detail];
});
const result = ss.getSheetByName(RESULT_SHEET_NAME) || ss.insertSheet(RESULT_SHEET_NAME);
result.clear();
result.getRange(1, 1, 1, 4).setValues([['セル', '項目', '種類', '内容']]).setFontWeight('bold');
if (rows.length > 0) {
result.getRange(2, 1, rows.length, 4).setValues(rows);
} else {
result.getRange(2, 1).setValue('問題は見つかりませんでした');
}
}コードの読み方(3か所だけ押さえればOK)
コードは長く見えますが、全部読まなくて大丈夫です。見てほしいのは、この3か所だけです。
1. 一番上の「設定」
シート名・列の位置・色などは、冒頭の const にまとめています。自分のシートに合わせるときは、ここだけ変えればOKです。列の位置は A列が0、B列が1…と数えます。
2. findIssues_:問題を探す部分
シートに触らず、2次元配列(getValues() で取得したデータ)だけを見て、問題の位置と種類を返します。
- 空欄:必須列が空か、空白だけのとき
- 形式:日付は
Dateとして読めるか、メールは「文字@文字.文字」の形か、数量は1以上の整数か - 重複:同じ値が出てきた行をすべて記録(最初の1件も含めて色が付くので、ペアで見比べられます)
3. 色付けは setBackgrounds で一括
セルに1つずつ色を付けると遅くなるため、データと同じ大きさの配列を作り、setBackgrounds で一度に反映しています。null を入れたセルは色がリセットされるので、直した後に再実行すれば、前回の色は消えます。
手順2:実行して権限を承認する
- スプレッドシートを再読み込みする(メニューに「入力チェック」が出ます)
- 「入力チェック」→「チェックを実行」をクリック
- 初回は「認証が必要です」と出るので「OK」を押し、自分のアカウントを選ぶ
- 「このアプリは Google で確認されていません」と表示されたら、左下の「詳細」→「(プロジェクト名)(安全ではないページ)に移動」を選ぶ。自分で作ったスクリプトを自分で動かすための表示なので、そのまま進んでよい
- 許可の内容を確認して「許可」(または「次へ」)を押す
「安全ではないページ」と出るとドキッとしますよね。ここ、最初は戸惑いやすいところです。でもこれは、自分で作ったスクリプトを自分で動かすための表示です。このスクリプトが求める許可は、スプレッドシートの参照と編集です。許可が終わると、そのまま実行されることがあります。色が付かないときは、慌てずに、もう一度メニューから実行してみてください。
メニューが出なくても焦らなくて大丈夫。Apps Scriptエディタで onOpen を一度実行すると表示されます。
手順3:結果を確認する
実行すると、問題のあるセルに色が付いて、「チェック結果」シートができあがります。サンプルデータなら、次のような一覧になります(イメージ)。
| セル | 項目 | 種類 | 内容 |
|---|---|---|---|
| C2 | メール | 重複 | 同じ値が2件あります |
| C3 | メール | 形式 | 「hanako@example」はメールアドレスの形式ではありません |
| A4 | 受付日 | 形式 | 「2026/13/40」は日付として読み取れません |
| B4 | 氏名 | 空欄 | 入力されていません |
| C4 | メール | 重複 | 同じ値が2件あります |
| D4 | 数量 | 形式 | 「0」は1以上の整数ではありません |
| A5 | 受付日 | 空欄 | 入力されていません |
| D5 | 数量 | 形式 | 「1.5」は1以上の整数ではありません |
| C6 | メール | 空欄 | 入力されていません |
色が付いたセルを直したら、もう一度メニューから実行してみましょう。直した分の色は消えるので、どこまで片付いたかがひと目でわかります。
毎日決まった時間に自動でチェックしてほしくなったら、時間主導トリガーの出番です。設定の流れは、こちらの記事が参考になります。
GASでGmailを自動整理!ラベル付け&定期実行トリガーで受信箱をスッキリさせる方法
自分のシートに合わせて変える
| 変えたいこと | 変更する場所 |
|---|---|
| チェックするシート名 | SHEET_NAME |
| 空欄NGにする列 | REQUIRED_COLS に列の位置を足す |
| 重複を見る列 | DUPLICATE_COL(例:注文番号の列) |
| 色を変える | COLORS |
| 列が増えたとき | COL に列を足し、findIssues_ に形式チェックを足す |
セルの背景色の基本を確認したいときは、こちらも読んでみてください。
つまずきやすい点
うまく動かないときは、まずこの表を見てみてください。たいていのつまずきは、ここに載っています。
| 症状 | 原因 | 対処 |
|---|---|---|
| 「シートが見つかりません」と出る | SHEET_NAME とシート名が違う(空白や全角半角も含む) | シート名を確認して、そろえる |
| 日付が「形式」エラーになる | セルが文字として入っている(先頭にアポストロフィなど) | セルの表示形式を「日付」にして、入力し直す |
| 数量が「1以上の整数ではありません」と出る | 数字が文字として入っている | 表示形式を「数値」にして入力し直す |
| メニューが出ない | スクリプトを貼った後に、シートを再読み込みしていない | ブラウザを再読み込みするか、onOpen を一度実行する |
| 前から付けていた手動の色が消えた | チェック範囲の背景色を毎回リセットしている | チェック用に別のシートを使うか、色の使い分けを決める |
まとめ
- 空欄・重複・形式のチェックは、ルールが決まっているのでGASに任せやすい
- 問題を探す処理(
findIssues_)と、シートに書く処理を分けておくと、条件を足しやすい - 色付けは
setBackgroundsで一括、一覧は別シートにまとめてジャンプできるようにする
次の一歩は、自分の名簿や一覧の「必須の列」と「重複を見る列」を1つずつ決めて、設定を書き換えて実行してみることです。最初は小さなシートで十分ですよ。一緒に、確認作業を減らしていきましょう。
関連する本・記事
今回のように「毎回同じ手順で確認している仕事」をGASで自動化したい方は、『Google Apps Script × 生成AI 全自動仕事術』も参考になります。GASと生成AIを組み合わせて、仕事の手作業を減らしたい方向けの1冊です。
- Excelで管理している方は、こちらの記事もどうぞ:メンバーがExcel VBAの書籍を書いたよ!『動くけど危ないExcel VBA』