こんにちは。イカPOです。
「最終受注日が10か月以上経過したデータを自動抽出&メール通知する方法」のスクリプト解説第2弾です。
前回は、「スプレッドシートからデータを抽出し、1行目をヘッダーとして扱う方法」でデータの取得を行いました。
今回は「1️⃣で取得したデータから、最終受注日が10か月以上経過した顧客を探す」を実装します。
スクリプトの処理ステップとしては今回は2️⃣に該当します。
1️⃣ 顧客管理表を取得(スプレッドシートのデータを取得)(前回)
2️⃣ 取得したデータから、最終受注日が10か月以上経過した顧客を探す ←今ここ
3️⃣一覧をメールで送信する (次回)
前回の記事はこちら
GASで条件をつけてデータを抽出する方法の考え方。
今回の目的は、(最終受注日から10か月以上経過した顧客)を見つけ出すことです。
顧客ID | 名前 | 最終受注日 | 最終受注項目 |
1 | テストタロウ | 2024/05/01 | ソケット |
2 | ヤマダタロウ | 2023/01/15 | 足部 |
3 | スズキタロウ | 2025/01/06 | ライナー |
このデータの中から10か月以上経過したものを抽出します。
執筆時点の日付が2025年2月なので、条件に一致するデータは2行目と3行目の「ヤマダタロウ」と「スズキタロウ」です。
つまり、スクリプトを使ってGoogleにやってもらいたい作業は、
最終受注日を検索して、条件に一致するデータを抽出することになります。
解説に進む前に
この記事から配列、戻り値、二次元配列などプログラミング用語がたくさんでてきます。
あえて細かい用語は解説しません。用語はぜひググってください。ググるのもプログラミングの醍醐味です。
データ抽出の方法
1️⃣ 「最終受注日」列を特定して取得する
2️⃣ 比較基準となる「現在の日付 + 10か月」のために「現在の日付自体」を準備する
3️⃣ 取得した各行の日付を1行ずつチェックし、条件に合致するか比較する
4️⃣ 条件に合致した行のデータ(顧客名や受注項目など)をまとめる
5️⃣ 抽出結果(10か月以上経過したデータ)を返す
このような手順が考えられます。前回作成したスクリプトに、この処理を追加していきましょう。
前回までの状態
function checkOverdueOrders() {
const ss = SpreadsheetApp.getActiveSpreadsheet(); // アクティブなスプレッドシートを取得
const sheet = ss.getSheetByName('一覧表'); // 操作対象のシート(適宜変更)
const data = sheet.getDataRange().getValues(); // シート全体のデータを取得(2次元配列)
const headers = data.shift(); // ヘッダー行(1行目)を分離して取得
Logger.log(data);
Logger.log(headers);
}
前回は関数「checkOverdueOrders()」を作成し、スプレッドシートをアクティブに設定して「一覧表」シートを取得、シート全体のデータとヘッダー行を取得するところまでを実装しました。
ここからは、「checkOverdueOrders()」に最終受注日をチェックし、条件を満たす行を抽出する部分を追加していきます。
コードの完成版
今回からはコードが長くなるので先に完成版をご覧ください。
function checkOverdueOrders() {
const ss = SpreadsheetApp.getActiveSpreadsheet(); // アクティブなスプレッドシートを取得
const sheet = ss.getSheetByName('一覧表'); // 操作対象のシート(適宜変更)
const data = sheet.getDataRange().getValues(); // シート全体のデータを取得(2次元配列)
const headers = data.shift(); // ヘッダー行(1行目)を分離して取得
Logger.log(data);
Logger.log(headers);
const targetHeader = "最終受注日"; // チェック対象のヘッダー名
const columnIndex = headers.indexOf(targetHeader); // '最終受注日' 列のインデックスを取得
if (columnIndex === -1) { // ヘッダーが見つからない場合
throw new Error(`ヘッダー "${targetHeader}" が見つかりません`);
}
const now = new Date(); // 現在の日付を取得
const overdueOrders = []; // 10か月以上経過したデータを記録する配列
// データ部分を1行ずつ処理
data.forEach((row, index) => {
const orderDate = row[columnIndex]; // 該当列(最終受注日)の値を取得
if (orderDate instanceof Date) { // 日付型であることを確認
const tenMonthsLater = new Date(orderDate);
tenMonthsLater.setMonth(tenMonthsLater.getMonth() + 10); // 10か月後の日付を計算
if (now > tenMonthsLater) { // 現在の日付が10か月後を過ぎている場合
const name = row[headers.indexOf('名前')]; // 名前列
const item = row[headers.indexOf('最終受注項目')]; // 最終受注項目列
overdueOrders.push(
`行 ${index + 2}: ${name} - ${formatDate(orderDate)} - ${item}`
);
}
}
});
Logger.log(overdueOrders);
return overdueOrders; // 結果(10か月以上経過した行情報)を返す
}
/**
* 日付を 'YYYY-MM-DD' 形式にフォーマット
* @param {Date} date 日付オブジェクト
* @return {string} フォーマット済みの日付文字列
*/
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
このコードを手順に沿って解説します。
1️⃣ 「最終受注日」列を特定して取得する
targetHeaderという変数に「最終受注日」という文字列を格納
const targetHeader = "最終受注日";
ここではスクリプト側で探したいヘッダー名(列名)を、ひとまず変数に入れておきます。「最終受注日」という文字列を直接何回も書く代わりに、targetHeader という変数にして使い回すイメージです。
headers配列の中から「最終受注日」がある位置を探す
const columnIndex = headers.indexOf(targetHeader);
headers はシートの1行目(ヘッダー行)を配列にしたものです。
headersの中身
[顧客ID, 名前, 最終受注日, 最終受注項目]
indexOf() は「配列の中で指定した要素が何番目にあるか」を返すメソッドです。
たとえば上の例なら “最終受注日” は 2番目(インデックス2)にあたります。
見つかった位置が columnIndex に代入されます。もし “最終受注日” が配列に存在しない場合は -1 が返ります。
なぜcolumnIndexが必要なのか
シート全体のデータを扱うときは、多くの場合「2次元配列」の形になります。
たとえば各行の中の columnIndex 番目の要素こそが、「最終受注日」の列のデータです。
つまり、columnIndex を知ることで「どの列が最終受注日に該当するか」を特定し、後ほどデータ抽出や条件チェックを行う際に便利になるわけです。
-1 だった場合の処理
if (columnIndex === -1) { // ヘッダーが見つからない場合
throw new Error(`ヘッダー "${targetHeader}" が見つかりません`);
}
indexOf() が見つからず -1 を返したら、「’最終受注日’ という列がヘッダーに存在しない」ことを意味します。
このようにエラーを投げることで、間違った列名を指定していないか、先に気づけるようになっています。
2️⃣ 比較基準となる「現在の日付 + 10か月」のために「現在の日付自体」を準備する
現在の日付から10か月以上経過しているデータを抽出するためには、「現在の日付」が必要です。
const now = new Date(); // 現在の日付を取得
現在の日付を取得するための関数 new Date()
new Date() は、スクリプトを実行したタイミングの「今日の日時」を取得するための組み込み関数です。
たとえば「2025年2月10日 15:30:00」のように、実行時点の情報を Date オブジェクトとして取得できます。
変数 now に格納して使い回す理由
now は「現在の日付」という重要な比較基準になるため、わざわざ変数にしておくことで後続の処理で何度でも利用できます。
たとえば、「最終受注日」から「現在の日付」までの月数を比較したり、メール送信する際の本文に反映したりする場合にも役立ちます。
「現在の日付 + 10か月」との関係
「最終受注日から10か月以上経過しているか」を判定するには、基準日となる「現在の日付」が必要になります。
後のステップでは、取得した now に対して「10か月後」の日付や日数を計算し、実際に orderDate と比較して「経過しているかどうか」を確認します。
ポイント
new Date() を呼び出すタイミングによって得られる日時が変わることに注意してください。定期実行(トリガー)などでスクリプトを動かした場合、スクリプトが動いた瞬間の日時が now となります。
もし数分おきや数日おきに処理を行う際、now はその都度異なる値を返します。
3️⃣ 取得した各行の日付を1行ずつチェックし、条件に合致するか比較する
準備が整ったので、いよいよデータを処理します。
顧客の情報が入っている配列 data をループ処理で1行ずつ見ていきましょう。
const overdueOrders = []; // 10か月以上経過したデータを記録する配列
// データ部分を1行ずつ処理
data.forEach((row, index) => {
const orderDate = row[columnIndex]; // 該当列(最終受注日)の値を取得
if (orderDate instanceof Date) { // 日付型であることを確認
const tenMonthsLater = new Date(orderDate);
tenMonthsLater.setMonth(tenMonthsLater.getMonth() + 10); // 10か月後の日付を計算
if (now > tenMonthsLater) { // 現在の日付が10か月後を過ぎている場合
const name = row[headers.indexOf('名前')]; // 名前列
const item = row[headers.indexOf('最終受注項目')]; // 最終受注項目列
}
}
});
data の中身を確認
現在 data には、下記のような2次元配列が入っています。スプレッドシート1行分が1つの配列として格納されているイメージです。
[
[1.0, テストタロウ, Wed May 01 00:00:00 GMT+09:00 2024, ソケット],
[2.0, ヤマダタロウ, Sun Jan 15 00:00:00 GMT+09:00 2023, 足部],
[3.0, スズキタロウ, Mon Jan 06 00:00:00 GMT+09:00 2025, ライナー]
]
スプレッドシートで見ると、こんな感じです。
顧客ID | 名前 | 最終受注日 | 最終受注項目 |
1 | テストタロウ | 2024/05/01 | ソケット |
2 | ヤマダタロウ | 2023/01/15 | 足部 |
3 | スズキタロウ | 2025/01/06 | ライナー |
この2次元配列の中から、現在の日付を基準に「10か月以上経過している」行を探すわけです。執筆時点で2025年2月なので、10か月以上経過しているのは2行目(ヤマダタロウ)。彼が抽出されれば成功ですね。
ループと最終受注日の取得
data.forEach((row, index) => {
const orderDate = row[columnIndex];
...
});
- data.forEach((row, index) => { … }) は、 data の要素を1つずつ row に取り出してループ処理する構文です。
- 変数 index には、今処理している行が何番目かが入ります。
- row[columnIndex] は「最終受注日」の列にあたる値を取得するための書き方です。先ほど特定した columnIndex を利用して、row の中から「最終受注日」を引っ張ってきています。
rowの中身について
rowの中身はdataの中身が1つずつ入っていきます。ループ内にLogger.log(row);を配置すると、下記のように1行ずつログに表示されます。確認してみてください。
[1.0, テストタロウ, Wed May 01 00:00:00 GMT+09:00 2024, ソケット]
[2.0, ヤマダタロウ, Sun Jan 15 00:00:00 GMT+09:00 2023, 足部]
[3.0, スズキタロウ, Mon Jan 06 00:00:00 GMT+09:00 2025, ライナー]
日付型かどうかをチェック
if (orderDate instanceof Date) {
...
}
- スプレッドシートのセルが文字列や数値だった場合、日付を操作しようとするとエラーになる可能性があります。
- そこで orderDate が本当に「日付型」かどうかを instanceof Date で確認し、日付でない場合はスキップ。日付であれば次の処理に進むようにしています。
10か月後の日付を作る
const tenMonthsLater = new Date(orderDate);
tenMonthsLater.setMonth(tenMonthsLater.getMonth() + 10);
- orderDate を元に、新しい日付オブジェクト tenMonthsLater を生成し、その月を +10 します。
- たとえば 2023年1月15日なら、 tenMonthsLater は 2023年11月15日 になります(年をまたぐ場合も JavaScript が自動調整)。
4️⃣ 条件に合致した行のデータ(顧客名や受注項目など)をまとめる
続いて、作成した tenMonthsLater と now を比較し、10か月以上経過しているかどうかを判定します。
if (now > tenMonthsLater) { // 現在の日付が10か月後を過ぎている場合
const name = row[headers.indexOf('名前')]; // 名前列
const item = row[headers.indexOf('最終受注項目')]; // 最終受注項目列
overdueOrders.push(
`行 ${index + 2}: ${name} - ${formatDate(orderDate)} - ${item}`
);
}
- if (now > tenMonthsLater) が true なら、「すでに最終受注日から10か月が経過している」状態です。
- このとき、 row から「名前」や「最終受注項目」を取り出し、 overdueOrders に格納します。
配列の位置関係と index + 2
- headers.indexOf(‘名前’) で「名前」列の位置を調べ、その位置のデータを取り出しています。
- index + 2 を使うのは、スプレッドシートの行番号に近い表記にするためです。
- 配列は 0 から始まりますが、スプレッドシートは 1 行目をヘッダーとして数えるので、この差分を調整しています。
overdueOrders.push(…) で配列に格納
overdueOrders.push(
`行 ${index + 2}: ${name} - ${formatDate(orderDate)} - ${item}`
);
最終的に事前に作成した overdueOrders
に値を格納しましょう。.pushで取得した値を1行ずつ順番に格納できます。
`行 ${index + 2}: ${name} – ${formatDate(orderDate)} – ${item}`で配列に格納する方法を指定します。
最終的に [行 3: ヤマダタロウ – 2023-01-15 – 足部] のように格納されます。
3
には${index + 2}
,
ヤマダタロウ
には ${name}
,
2023-01-15
には ${formatDate(orderDate)}
,
足部
には ${item}
それぞれ が対応しています。
formatDate(orderDate)について
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
- formatDate() は日付を YYYY-MM-DD の文字列に変換するための関数です。今回は詳細解説を省略しますが、「日付をスクリプトで見やすく整形する処理」と考えてください。
5️⃣ 抽出結果(10か月以上経過したデータ)を返す
最後に、関数全体の終わりで結果を返します。
return overdueOrders; // 結果(10か月以上経過した行情報)を返す
- ここで overdueOrders を返すことで、関数 checkOverdueOrders() を呼び出した側で「どの行が10か月以上経過しているか」を取得できるようになります。
- 後のステップで、このデータを使ってメール送信したり、別シートに転記したりするわけです。
まとめ
この記事では、特定の列を検索し、条件をつけてデータを抽出する方法について紹介しました。
✅ 「最終受注日」列のデータを取得
✅ 今の日付を取得する(10か月以上経過の起点にするため)
✅ 条件を指定してデータを抽出する
✅ 抽出したデータを格納する
これでメールを送信する準備は整いましたね。
次回予告
次回の記事では、「作成したデータをメールで送信する方法」について詳しく解説します!
この記事についてのご意見や間違いのご指摘は、ぜひX(旧Twitter)でお知らせください!
皆さまのフィードバックをお待ちしています。以下のアカウントまでお気軽にメッセージをお寄せください。
この記事には筆者の個人的な解釈も一部含まれています。一つの参考としてお読みいただきつつ、最終的にはご自身や担当の方としっかり相談の上で判断いただけますと幸いです。
皆様の声で情報をアップデートしていきます。よろしくお願いします。