GASで入力ミスを自動で見つける|スプレッドシートの確認作業を減らす方法

受付名簿や注文一覧をスプレッドシートで管理していると、「あれ、この人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:スクリプトエディタにコードを貼る

  1. スプレッドシートのメニュー「拡張機能」→「Apps Script」を開く
  2. 最初からある function myFunction() {} を消す
  3. 下のコードをすべて貼り付けて、保存する

  • 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('問題は見つかりませんでした');
  }
}

Apps Scriptエディタに、コードを貼り付けて保存した状態

コードの読み方(3か所だけ押さえればOK)

コードは長く見えますが、全部読まなくて大丈夫です。見てほしいのは、この3か所だけです。

1. 一番上の「設定」

シート名・列の位置・色などは、冒頭の const にまとめています。自分のシートに合わせるときは、ここだけ変えればOKです。列の位置は A列が0、B列が1…と数えます。

2. findIssues_:問題を探す部分

シートに触らず、2次元配列(getValues() で取得したデータ)だけを見て、問題の位置と種類を返します。

  • 空欄:必須列が空か、空白だけのとき
  • 形式:日付は Date として読めるか、メールは「文字@文字.文字」の形か、数量は1以上の整数か
  • 重複:同じ値が出てきた行をすべて記録(最初の1件も含めて色が付くので、ペアで見比べられます)
3. 色付けは setBackgrounds で一括

セルに1つずつ色を付けると遅くなるため、データと同じ大きさの配列を作り、setBackgrounds で一度に反映しています。null を入れたセルは色がリセットされるので、直した後に再実行すれば、前回の色は消えます。


手順2:実行して権限を承認する

  1. スプレッドシートを再読み込みする(メニューに「入力チェック」が出ます)
  2. 「入力チェック」→「チェックを実行」をクリック
  3. 初回は「認証が必要です」と出るので「OK」を押し、自分のアカウントを選ぶ
  4. 「このアプリは Google で確認されていません」と表示されたら、左下の「詳細」→「(プロジェクト名)(安全ではないページ)に移動」を選ぶ。自分で作ったスクリプトを自分で動かすための表示なので、そのまま進んでよい
  5. 許可の内容を確認して「許可」(または「次へ」)を押す

「安全ではないページ」と出るとドキッとしますよね。ここ、最初は戸惑いやすいところです。でもこれは、自分で作ったスクリプトを自分で動かすための表示です。このスクリプトが求める許可は、スプレッドシートの参照と編集です。許可が終わると、そのまま実行されることがあります。色が付かないときは、慌てずに、もう一度メニューから実行してみてください。

再読み込みすると、メニューに「入力チェック」が出る
初回は「認証が必要です」と表示される。OKを押して進む

メニューが出なくても焦らなくて大丈夫。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_ に形式チェックを足す

セルの背景色の基本を確認したいときは、こちらも読んでみてください。

GoogleAppsScriptでセルの背景色を変更する


つまずきやすい点

うまく動かないときは、まずこの表を見てみてください。たいていのつまずきは、ここに載っています。

症状 原因 対処
「シートが見つかりません」と出る SHEET_NAME とシート名が違う(空白や全角半角も含む) シート名を確認して、そろえる
日付が「形式」エラーになる セルが文字として入っている(先頭にアポストロフィなど) セルの表示形式を「日付」にして、入力し直す
数量が「1以上の整数ではありません」と出る 数字が文字として入っている 表示形式を「数値」にして入力し直す
メニューが出ない スクリプトを貼った後に、シートを再読み込みしていない ブラウザを再読み込みするか、onOpen を一度実行する
前から付けていた手動の色が消えた チェック範囲の背景色を毎回リセットしている チェック用に別のシートを使うか、色の使い分けを決める

まとめ

  • 空欄・重複・形式のチェックは、ルールが決まっているのでGASに任せやすい
  • 問題を探す処理(findIssues_)と、シートに書く処理を分けておくと、条件を足しやすい
  • 色付けは setBackgrounds で一括、一覧は別シートにまとめてジャンプできるようにする

次の一歩は、自分の名簿や一覧の「必須の列」と「重複を見る列」を1つずつ決めて、設定を書き換えて実行してみることです。最初は小さなシートで十分ですよ。一緒に、確認作業を減らしていきましょう。


関連する本・記事

今回のように「毎回同じ手順で確認している仕事」をGASで自動化したい方は、『Google Apps Script × 生成AI 全自動仕事術』も参考になります。GASと生成AIを組み合わせて、仕事の手作業を減らしたい方向けの1冊です。