スプレッドシート集計の数え間違いを更新前に発見|GASで状態別件数をdry-run確認
ツールおじ|GoogleアカウントをAIエージェント化
進捗表や公開準備表を集計するとき、「完了」と「完了済み」が混ざっていたり、同じ項目IDが2行あったりすると、集計式が動いていても数字は正しくなりません。誤った件数を報告用シートへ上書きする前に、まず入力の不備と集計予定値を確認したいところです。
この記事では、テスト用スプレッドシートの行を読み取り、区分×状態の件数をdry-runで集計する無料GASを紹介します。セルの更新、シート追加、メール送信は行わず、問題がある行は値をログへ出さずに行番号と理由だけを返します。
先に知っておきたいこと
- 対象は、開いているスプレッドシート内の「公開準備」シートです。
- 初期設定ではデータ500行までを確認し、超えた場合は停止します。
- 許可する状態は「未着手」「作業中」「確認待ち」「完了」の4種類です。
- 期限は空欄または `yyyy-MM-dd` 形式で入力します。
- 項目ID、担当名、内容などの値はログへ出しません。
- この例は読み取り専用です。集計結果の書き込みや自動通知は行いません。
準備するシート
個人情報や機密情報を含まないテスト用スプレッドシートを作り、シート名を「公開準備」にします。1行目は次の5列を、この順番で用意してください。
- 項目ID
- 区分
- 状態
- 担当
- 期限
たとえば、区分には「本文」「画像」「リンク」「設定」、状態には許可した4種類のいずれかを入れます。動作確認用として、状態の表記ゆれや重複した項目IDも少量だけ含めると、停止条件を確認できます。
コピーして使える最小コード
function auditSpreadsheetSummaryFree() {
const CONFIG = {
DRY_RUN: true,
SHEET_NAME: '公開準備',
MAX_ROWS: 500,
ALLOWED_STATUSES: ['未着手', '作業中', '確認待ち', '完了']
};
if (CONFIG.DRY_RUN !== true) {
throw new Error('この無料例はDRY_RUN=trueのまま使用してください。');
}
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.SHEET_NAME);
if (!sheet) {
throw new Error('公開準備シートが見つかりません。');
}
const values = sheet.getDataRange().getDisplayValues();
if (values.length < 2) {
throw new Error('見出し行の下にテストデータを1行以上用意してください。');
}
const expectedHeaders = ['項目ID', '区分', '状態', '担当', '期限'];
const actualHeaders = values[0].map(function (value) {
return String(value).normalize('NFKC').trim();
});
const headersMatch = expectedHeaders.every(function (header, index) {
return actualHeaders[index] === header;
});
if (!headersMatch) {
throw new Error('1行目の見出し名または列順が違います。');
}
const rows = values.slice(1).filter(function (row) {
return row.some(function (value) {
return String(value).trim() !== '';
});
});
if (rows.length > CONFIG.MAX_ROWS) {
throw new Error('確認上限を超えました。対象を分けて再実行してください。');
}
const seenIds = {};
const summary = {};
const issues = [];
let validRows = 0;
rows.forEach(function (row, index) {
const rowNumber = index + 2;
const itemId = String(row[0]).normalize('NFKC').trim();
const category = String(row[1]).normalize('NFKC').trim();
const status = String(row[2]).normalize('NFKC').trim();
const owner = String(row[3]).normalize('NFKC').trim();
const dueDate = String(row[4]).normalize('NFKC').trim();
const codes = [];
if (!itemId || !category || !status || !owner) codes.push('REQUIRED_EMPTY');
if (itemId && seenIds[itemId]) codes.push('DUPLICATE_ITEM_ID');
if (itemId) seenIds[itemId] = true;
if (status && CONFIG.ALLOWED_STATUSES.indexOf(status) === -1) codes.push('STATUS_NOT_ALLOWED');
if (dueDate && !isValidIsoDate_(dueDate)) codes.push('DATE_FORMAT_INVALID');
if (codes.length) {
issues.push({ row: rowNumber, codes: codes });
return;
}
if (!summary[category]) summary[category] = {};
summary[category][status] = (summary[category][status] || 0) + 1;
validRows += 1;
});
const issueCounts = issues.reduce(function (counts, issue) {
issue.codes.forEach(function (code) {
counts[code] = (counts[code] || 0) + 1;
});
return counts;
}, {});
const result = {
dryRun: true,
checkedRows: rows.length,
validRows: validRows,
issueRows: issues.length,
issueCounts: issueCounts,
issues: issues.slice(0, 50),
summary: summary
};
console.log(JSON.stringify(result));
return result;
}
function isValidIsoDate_(value) {
const match = String(value).match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) return false;
const year = Number(match[1]);
const month = Number(match[2]);
const day = Number(match[3]);
const date = new Date(Date.UTC(year, month - 1, day));
return date.getUTCFullYear() === year
&& date.getUTCMonth() === month - 1
&& date.getUTCDate() === day;
}実行前の準備
1. テスト用スプレッドシートに「公開準備」シートを作ります。
2. 見出し5列と、個人情報を含まないサンプル行を入力します。
3. 拡張機能からApps Scriptを開き、コードを貼り付けます。
4. `DRY_RUN` が `true`、`MAX_ROWS` が500であることを確認します。
5. `auditSpreadsheetSummaryFree` を選び、手動で実行します。
6. 実行ログの `issueRows` と `issueCounts` を先に確認します。
7. 問題行を手動で直した後に再実行し、`summary` の件数を元表と照合します。
ログの読み方
- `checkedRows`: 空行を除いて確認した行数です。
- `validRows`: 入力検証を通過し、集計へ含めた行数です。
- `issueRows`: 1件以上の問題が見つかった行数です。
- `issueCounts`: 空欄、ID重複、状態の許可値外、日付形式不正の件数です。
- `issues`: 最大50件まで、行番号と理由コードだけを表示します。
- `summary`: 区分ごとの状態別件数です。
値そのものをログへ出していないため、どの行を直すかは行番号でシートへ戻って確認します。`issueRows` が1件でもある場合は、集計値を報告や公開判断へ使わないでください。
集計結果を使う前のチェックリスト
- 見出し名と列順が想定どおりか確認する。
- 状態の表記を4種類へ統一する。
- 重複した項目IDが別作業を表していないか人が確認する。
- 空欄行を勝手に「未着手」とみなさない。
- `validRows + issueRows` が `checkedRows` と一致するか確認する。
- 状態別件数を少量の元データと手計算で照合する。
- 問題がないことを確認してから、必要なら別工程で集計表を更新する。
停止条件
- 対象シートが見つからない。
- 見出しまたは列順が違う。
- 500行を超えている。
- `issueRows` が1件以上ある。
- 集計件数と目視確認が一致しない。
- 実データに個人情報や機密情報が含まれ、ログ範囲を判断できない。
該当した場合は、許可値を広げたり問題行を除外したりして続行せず、入力ルールと対象範囲を確認してください。
制限と安全上の注意
このコードは表示値を使うため、数式の計算式そのものや変更履歴は監査しません。また、区分と状態の件数を確認するだけで、作業の完了品質や公開可否を自動判断するものではありません。
この無料例はセル更新、シート作成、メール送信、外部API通信、時間主導トリガー作成を行いません。集計結果の自動書き込みを追加する前に、コピー環境で入力検証と再実行時の挙動を確認してください。
まとめ
集計処理では、数式やコードが動くことよりも、入力の空欄・表記ゆれ・重複を先に見つけることが重要です。まずは読み取り専用のdry-runで問題行と状態別件数を確認し、元表と照合してから次の更新工程へ進めてください。
公開準備の入力確認を仕組み化したい方へ
個人クリエイター公開準備チェックGAS Proを見る

GasおじのTips商品一覧を見る
GasおじのTips商品一覧を見る

※このコードは学習・検証用の例です。実データへ適用する前にコピー環境で確認してください。Google公式・Google公認の商品ではありません。
