Googleフォームのプルダウン選択肢を自動更新する方法

GAS

会食メモをGoogleフォームで自動化する方法-ビジネスパーソン向け|会食後3分で記録できる仕組み-」を作り、会食メモを自動化しました。

ただ、会う人が増える度にプルダウンの項目を増やすのは面倒なので、Peopleシートから「相手」候補を自動生成・同期するGASを作成しました。

スポンサーリンク
スポンサーリンク

はじめに|Googleフォームの「選択肢更新」が面倒すぎる問題

Googleフォームは、社内外のヒアリングや業務記録に便利です。
しかし、使い続けると必ず発生するのが「選択肢のメンテナンス地獄」です。

例えば、会食メモや面談ログのフォームで、相手をプルダウンで選ばせたい場合。

  • 相手が増えるたびにフォーム編集が必要
  • 追加し忘れが起きる
  • 表記ゆれ(会社名の表記など)が起きる
  • 人物が増えるほど更新作業が重くなる

この状態だと、フォーム入力の仕組み自体が続かなくなります。

そこで本記事では、Peopleシート(人物一覧)をマスタにして、フォームの「相手」プルダウン選択肢を自動生成・同期する方法を解説します。
個人利用を前提にしているため、チーム利用の権限設計などは省き、最短で動く・壊れにくい構成にしています。

今回作るもの(仕組みの全体像)

やることは単純です。

  1. Peopleシートから人物一覧を読み込む
  2. フォームの「相手(選択)」質問を探す
  3. 生成した候補リストをプルダウンにセットする
People(スプレッドシート)
  └ person_id / name / company …
        ↓
GASで候補文字列を生成
        ↓
Googleフォームの「相手(選択)」プルダウンに反映

この仕組みが活かせる対象(ビジネスパーソン向け)

この自動同期は、会食メモに限らないです。

「人を選ぶフォーム」が存在する仕事ならすべて対象です。

  • 会食・面談・打ち合わせのログ
  • 商談記録
  • 顧客ヒアリング
  • 相談受付フォーム(士業・個人事業主)
  • 社内の1on1記録

“入力を仕組みにする”という意味で、業務効率化の効果が大きいです。

Step1:フォームIDを取得する

GASでフォームを操作するにはフォームIDが必要です。以前に作成したフォームのIDを取得します。

フォーム編集画面URLは次の形式です。

https://docs.google.com/forms/d/<FORM_ID>/edit

この <FORM_ID> の部分をコードに設定します。

Step2:GASでPeople→フォーム選択肢を同期する(完成コード)

スプレッドシートのApps Scriptに貼り付けて使えます。前回同様に同じプロジェクト内に別名で保存して下さい。

注意:すでに他のコードで SHEET_PEOPLE などを定義している場合は、同名の const を二重定義しないでください(SyntaxErrorの原因になります)。

/** ===== People → Googleフォーム「相手」選択肢 同期(個人版) ===== */

// 既に定義済みなら、この行は削除してください
// const SHEET_PEOPLE = "People";

// フォームID(edit URLの /d/ と /edit の間)
const FORM_ID = "*********";

// フォーム内の「相手」質問タイトル(完全一致)
const FORM_PERSON_QUESTION_TITLE = "相手";

// 生成する選択肢の最大数(安全策)
const MAX_CHOICES = 500;

/**
 * スプレッドシートを開いた時にメニューを追加
 */
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu("会食メモ")
    .addItem("フォームの相手候補を同期", "syncPeopleToFormChoices")
    .addSeparator()
    .addItem("フォームIDの確認(ログ)", "logFormIdHelp")
    .addToUi();
}

/**
 * Peopleシートから候補を作り、フォームのプルダウン(相手)へ同期する
 */
function syncPeopleToFormChoices() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sh = ss.getSheetByName(SHEET_PEOPLE);
  if (!sh) throw new Error(`Sheet not found: ${SHEET_PEOPLE}`);

  const people = loadPeopleForChoices_(sh);
  if (people.length === 0) {
    SpreadsheetApp.getUi().alert("Peopleに有効なデータがありません。person_id と name を確認してください。");
    return;
  }

  if (people.length > MAX_CHOICES) {
    SpreadsheetApp.getUi().alert(`候補数が多すぎます(${people.length}件)。MAX_CHOICES=${MAX_CHOICES} を調整してください。`);
    return;
  }

  const form = FormApp.openById(FORM_ID);
  const item = findListItemByTitle_(form, FORM_PERSON_QUESTION_TITLE);
  if (!item) {
    SpreadsheetApp.getUi().alert(`フォームに質問「${FORM_PERSON_QUESTION_TITLE}」が見つかりません。質問タイトルを一致させてください。`);
    return;
  }

  // 重複排除・並び替え(会社→名前の順に見やすく)
  const choices = buildChoices_(people);

  item.setChoiceValues(choices);

  SpreadsheetApp.getUi().alert(`同期完了:${choices.length}件の候補をフォームに反映しました。`);
}

/** ===== 内部関数 ===== */

/**
 * Peopleシートからフォーム用候補データを読み込む
 * 必須:person_id(A列)・name(B列)
 * 会社(D列)は任意、表示に使う
 */
function loadPeopleForChoices_(sh) {
  const values = sh.getDataRange().getValues();
  values.shift(); // header

  return values
    .filter(r => r[0] && r[1]) // person_id, name必須
    .map(r => ({
      person_id: String(r[0]).trim(),
      name: String(r[1]).trim(),
      company: r[3] ? String(r[3]).trim() : ""
    }));
}

/**
 * 表示用の選択肢配列を作成
 * 形式:P0001|山田 太郎(○○商事)
 */
function buildChoices_(people) {
  // 正規化&重複排除(person_idでユニーク)
  const map = new Map();
  people.forEach(p => {
    const key = p.person_id;
    if (!map.has(key)) map.set(key, p);
  });

  const arr = Array.from(map.values());

  // 並び:会社→名前→ID
  arr.sort((a, b) => {
    const ca = a.company || "";
    const cb = b.company || "";
    if (ca !== cb) return ca.localeCompare(cb, "ja");
    if (a.name !== b.name) return a.name.localeCompare(b.name, "ja");
    return a.person_id.localeCompare(b.person_id);
  });

  return arr.map(p => {
    const comp = p.company ? `(${p.company})` : "";
    return `${p.person_id}|${p.name}${comp}`;
  });
}

/**
 * フォーム内から「プルダウン(LIST)」項目をタイトル一致で検索
 * ※プルダウン以外(複数選択等)にした場合は型が変わるので注意
 */
function findListItemByTitle_(form, title) {
  const items = form.getItems(FormApp.ItemType.LIST);
  for (const it of items) {
    const li = it.asListItem();
    if (li.getTitle() === title) return li;
  }
  return null;
}

/**
 * フォームIDが分からない時用(ログにヒントを出すだけ)
 */
function logFormIdHelp() {
  Logger.log("フォーム編集URLは https://docs.google.com/forms/d/<FORM_ID>/edit です。<FORM_ID> をFORM_IDに設定してください。");
  SpreadsheetApp.getUi().alert("ログにフォームID取得のヒントを出しました。Apps Scriptのログを確認してください。");
}

Step3:実行手順(初心者向け)

  1. Apps Scriptを保存する
  2. スプレッドシートを再読み込みする
  3. メニューに「会食メモ」が出る
  1. 「フォームの相手候補を同期」をクリックする
  1. フォームのプルダウンが更新される

これだけです。

よくあるエラーと対処法(初心者がつまずく所)

1)フォームに質問が見つからない

原因:質問タイトルが一致していない
対処:フォーム側のタイトルを確認し、FORM_PERSON_QUESTION_TITLE を合わせる

2)二重定義エラー(Identifier has already been declared)

原因:同名の const を複数のコードで宣言している
対処:定数はプロジェクト内で1回だけにする(片方を削除)

3)候補が増えない

原因:Peopleシートで person_id / name が空の行がある
対処:必須列が埋まっているか確認

まとめ|Googleフォームの選択肢は“仕組み化”すると継続できる

Googleフォームのプルダウン選択肢を手動で管理すると、必ず運用が崩れます。
しかし、PeopleシートをマスタにしてGASで同期すれば、

  • 追加・修正はPeopleだけ
  • フォームはワンクリック更新
  • 表記ゆれが減る
  • 仕組みが続く

という状態を作れます。

会食メモ、面談ログ、商談記録など、「人を選ぶフォーム」を運用している方は、ぜひ導入してみてください。

タイトルとURLをコピーしました