0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GAS】Googleフォームの質問変更に負けない!「部分一致」と「秘密の暗号」でcleanFormData(e)の防御力を鉄壁に

0
Last updated at Posted at 2026-03-19

今までの記事一覧

  1. GASでGoogleForm回答を取得するなら lastRow?(e)?試してみた
  2. onFormSubmit(e)を手動実行でデバッグする方法
  3. どっちを使う?onFormSubmit(e)の values と namedValues の違いと使い分け
  4. onFormSubmit(e) の e.values 配列順のしくみ
  5. Googleフォームで質問を変えても壊れない!cleanFormData(e)でnamedValues防御力をアップ
  6. Googleフォームの質問変更に負けない!「部分一致」と「秘密の暗号」でcleanFormData(e)の防御力を鉄壁に <この記事
  7. 手動コピペはもう卒業!Googleフォームの回答別に処理を自動仕分け
  8. Googleフォームで同時に大量送信されても踏ん張る!LockServiceで順番制御!try - catch - finally でバトンを繋げ!
  9. LockServiceでは順番は守れない?受付番号で順序を保証する方法

前提

この記事は、フォーム回答を保存している スプレッドシート側のGAS を前提にしています。
トリガーは以下を設定しています。

  • スプレッドシートから
  • フォーム送信時

おさらい:cleanFormData(e)完全一致で堅牢化

前回は ユーティリティ関数cleanFormData(e)を使うことで、質問文が変わってもエラーでプログラムが止まらない仕組みを作りました。

でも、エラーにならなくても  データが空っぽ だったら困りますよね。
今回はさらに一歩進んで、多少の変更があっても、中身のデータをちゃんと拾い上げる、本当の意味で最強のコードに進化させます!

「だいたい合ってればOK」にする!部分一致マッチング戦略

前回作ったcleanFormData(e) はこれ。

function cleanFormData(e) {
  const namedValues = e.namedValues;
  
  // ここで「フォームの質問文」と「プログラムで使いたい名前」を紐付ける
  return {
    // 左側:プログラムで使う名前 / 右側:実際のフォームの質問文
    email: namedValues["メールアドレス"] ? namedValues["メールアドレス"][0] : "",
    name:  namedValues["お名前"] ? namedValues["お名前"][0] : "",
    body:  namedValues["お問い合わせ内容"] ? namedValues["お問い合わせ内容"][0] : ""
  };
}

ここでフォームの質問分が「お名前を入力してください」に変更されたら?
エラーにはなりませんが空欄になってしまい、欲しいデータが取得できません。
質問文が「お名前」でも「お名前をどうぞ」でも「名前を入れてください」でもヒットさせたいですよね。
じゃあいっそ 「名前」が入ってたらOKってことにしちゃいましょう。

for文とincludesでcleanFormData(e)を部分一致型に

ユーティリティ関数cleanFormData(e)をこうしてみます。

function cleanFormData(e) {
  const namedValues = e.namedValues;
  const data = {};

  // 1. 全ての質問項目をループ
  for (let key in namedValues) {  //keyには「質問文」が入っている
    const value = namedValues[key][0];
    
    // 2. 質問文の中に「メール」が入ってたら email に入れる
    if (key.includes("メール")) {
      data.email = value;
    }    
    // 3. 質問文の中に「名前」が入ってたら name に入れる
    else if (key.includes("名前")) {
      data.name = value;
    }
    // 4. すべての質問を「質問文」で保存
    // これを書いておけば、項目が増えても メイン関数側で
    // formData["住所を入力してください"] みたいにすれば呼び出せる
    data[key] = value;
  }

  return data;
}

質問文に

  • 「メール」が入ってたら email という名前で保存
  • 「名前」が入ってたら name という名前で保存

これは長い質問文に「email」「name」というニックネームを付けるイメージです。

ニックネームを付けない項目は「質問文そのまま」で呼び出します。

では実験してみましょう。

フォームはこんな感じで
image.png
前回と同じようにメイン関数でログを取ってみます。実行!

function onFormSubmit(e) {
  const formData = cleanFormData(e);

  Logger.log("email:"+ formData.email); //ニックネームが付いているものはニックネームで
  Logger.log("name:"+ formData.name); 
  Logger.log("body:"+ formData["お問い合わせ内容をどうぞ"]); //ニックネームがないものは質問文で
}

見事!データ取得に成功しました。
image.png
「メールアドレス」「お名前を入力してください」はそれぞれemail nameで呼び出すことに成功。
ニックネームが付いていない「お問い合わせ内容をどうぞ」はformData["お問い合わせ内容をどうぞ"]で取得することができました。

質問が増えてもらくちん

前回のこの書き方は、質問が増えたとき、取得したい質問の数だけ三項演算子を追加する必要がありました。
つまり、項目が増えるたびに記述が増え、保守性が下がるという問題です。

return {
  email: namedValues["メール"] ? namedValues["メール"][0] : "",
  name:  namedValues["名前"] ? namedValues["名前"][0] : "",
  age:   namedValues["年齢"] ? namedValues["年齢"][0] : "", // 追加
  tel:   namedValues["電話"] ? namedValues["電話"][0] : "", // 追加
  body:  namedValues["内容"] ? namedValues["内容"][0] : ""  // 追加
  //この下にどんどん追加
};

今回ご紹介した新しいcleanFormData(e)では、for文ですべての質問を自動的に[0]を外した形で保存し、 formData["質問文"]で取得することができます。
その中で、ニックネームで呼びたいと思うものは、これをifelse if の列の中にコピペして、★部分を書き換えればOK。

else if (key.includes("")) {
  data. = value;
}

もっと簡単に書くなら1行でこれでもOK。

else if (key.includes("")) { data. = value; }

ここまでのまとめ

つまり、ここが進化

  • クリーン化([0]を外す作業)の共通化
    前回はすべての行で [0] や ? : "" を書いていましたが、今回はループの頭で1回やるだけ。これでコードが劇的にスッキリします。
  • 「とりあえず保存」の安心感
    前回は return に書き忘れたデータは消えてしまいましたが、今回は data[key] = value; の中に全部の質問文と回答が入っているので、if 文を書き忘れても formData["質問文"] で呼び出せます。
    むしろ、ニックネームで呼びたいものだけ、ifelse ifの列の中にコピペして入れればOK。

でもこれって「名前を入れて」「名前(ふりがな)」ってあったら両方ともヒットするんちゃう?

そうなのよ!なのでここでもう1つ、部分一致を使ったテクをご紹介します。

質問に暗号を仕込め

まず、「名前を入れて」「名前(ふりがな)」だけならif文の順番を入れ替えて
else if (key.includes("ふりがな")) の後に
else if (key.includes("名前")) を入れれば解決するけどね。

今回は別のアプローチとして、今覚えたばかりの部分一致を使う方法をやってみましょう。

フォームの質問文に暗号を仕込みます。

今回は暗号 §name §kanaを使いましょう。
「お名前を入力してください。§name」「お名前(ふりがな)を入れてください §kana」のように、質問文の末尾に暗号を入れます。
暗号は他の質問と被らないように注意してね。§name/§nametakeみたいに一部だけの一致もNGです。

image.png

暗号は、「§name」でもいいし「〈q1〉」でもいいし、自分がわかりやすいもので他の質問と違っていればなんでもOK。

そしてこれを部分一致のキーとして呼び出します。

function cleanFormData(e) {
  const namedValues = e.namedValues;
  const data = {};

  // 1. 全ての質問項目をループ
  for (let key in namedValues) {
    const value = namedValues[key][0];
    // 2. もし質問の中に「§email」が入ってたら、一律で email に入れる(メール部分)
    if (key.includes("§email")) {
      data.email = value;
    }    
    // 3. もし「§name」が入ってたら name に入れる(名前部分)
    else if (key.includes("§name")) {
      data.name = value;
    }
    // 4. もし「§kana」が入ってたら kana に入れる(ふりがな部分)
    else if (key.includes("§kana")) {
      data.kana = value;
    }
    // 5. すべての質問を「質問文」で保存
    data[key] = value;
  }

  return data;
}

この「暗号(ID)方式」の真のすごさは、
質問文をどれだけいじっても、コードを1文字も直さなくていい ことにあります。

  • 「お名前 §name」が
  • 「【必須】お名前を入力してください §name」に変わっても
  • 「Name(English Please) §name」にされても

これ全部、プログラム側は includes("§name") だけでキャッチできちゃうんです。
暗号 §name が付いてるから。

そして

  • 「家族のお名前 §family」
  • 「会社の名前 §company」

なんかは「名前」と付いていても §name がないから拾わない。
最強すぎない?

フォームに表示されちゃうけどね

ただ、この案のデメリットは、こんなふうに暗号がフォームに表示されてしまうこと。

image.png

回答者に「§kanaってなんぞ?」と思われない工夫が必要です。

例えば....

  • さりげない系:(ID:01) , [Q1]
  • デザイン系:|email , ‣ name
  • 「そういう仕様です」系: (管理番号: A-1)

あなたのセンスが問われます(笑)

あ、あたりまえだけど、フォームの質問順が変わったからと言って[Q1]の数字を変えちゃダメですよ。
フォームの表示が[Q3][Q1][Q5]みたいな並びになってしまったとしてもそこはぐっと我慢して、暗号は絶対変えないでね。

まとめ

というわけで、今回はフォームの質問を部分一致で取得する方法でした。
それぞれメリット、デメリットがありましたね。

実務のシチュエーションに合わせて、最適な防御策を選べるよう表にしてみました。

手法 メリット(できること) デメリット(注意点) おすすめのシーン
完全一致 コードが直感的で、初心者でも読みやすい。 質問文が1文字でも変わるとデータが取れなくなる。 質問文が絶対に変わらない、自分だけが管理するフォーム
部分一致 「名前」「メール」等の言葉があれば、多少の文言変更をスルーできる。 「名前」と「名前(ふりがな)」等、似た言葉が複数あると混ざるリスクがある。 運用者が語尾などを微調整する、一般的なフォーム
暗号方式 質問文をどれだけ変えても、IDさえあれば100%確実に取得できる。 設問に「§name」等が見えてしまい、少し不格好。運用ルールを徹底する必要がある。 質問文が頻繁に変わる、または項目数が非常に多いプロ仕様のフォーム

迷ったときは?

最初は「部分一致」で作り始めてみて、もし「項目が被ってうまく判定できない!」となった特定の項目だけ「暗号方式(ID)」に切り替える……というハイブリッドな運用もいいかもしれませんね!


以上です。
「もっと良い方法があるよ!」って方はコメントで教えていただけると嬉しいです。
この記事が、同じような悩みを持った方の助けになりますように。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?