プロが書く美しいコードじゃない、素人の稚拙なコードだからこそ、得られるものだってきっとある…そんな思いでコードを公開しています。どうぞ生暖かく見守ってください。

[Google Apps Script]テスト形式のGoogleフォームでアイテムタイプごとに共通処理を振り分ける

[tool1]フォームをシートに書き出しGoogle Apps Script
  • 分岐処理その2(switch構文)
  • 「===」の意味は?「==」との違いは?
  • 論理式
  • データ型
  • typeof

前回は、選択肢を持つタイプについてはその内容を取得して書き込むところまで進みました。

現状のコードでは、アイテムタイプがラジオボタンか否かだけで判別していましたが、選択肢を持つアイテムタイプは他にもありますので、「もしもラジオボタンなら」の部分を「もしもラジオボタンかチェックボックスかプルダウンか…」という形にして、同じように選択肢を持つアイテムにも対応させたいところです。

ただ、ここで問題になるのが、共通操作の前にそれぞれのタイプにキャストする「as*****Item();」という処理が必要になるということです。ここはそれぞれのアイテムタイプごとに*****の部分が異なったメソッドを書く必要があります。

for文の中の流れを上記のように少し変えます。

    if (itemType == 'MULTIPLE_CHOICE') {
      testPartsItem = itemparts.asMultipleChoiceItem();

の部分だけを引っ張ってきて、すべてのタイプをそれぞれに応じたアイテムタイプにキャスト(変身)させてしまいましょう。

「もしアイテムタイプが●●ならば〇〇、▲▲ならば△△・・・」というような分岐は、ifでも可能ですが、もう一つの条件分岐「swich」という構文も使用できます。

以下のコードは正常に動きません。ただ、間違いの過程も出した方が同じ間違いをした人の原因究明の助けになるかと思いますので、そのまま掲示します。最後まで読み進めてください。
    //swichによりそれぞれのアイテムにキャスト
    switch (itemType) {
      case 'CHECKBOX':
        testPartsItem = itemparts.asCheckboxGridItem();
        break;
      case 'CHECKBOX_GRID':
        testPartsItem = itemparts.asCheckboxItem();
        break;

このような感じになります。
現在取得できるアイテムタイプは17種類ですが、ファイルアップロードタイプのアイテムにはキャストできません。(固有の動きがないから?)

キャストした後に、それぞれをいくつかのグループに分けて目印をつけておくとよさそうです。

選択肢がないアイテムに対してgetChoices()…なんてやってしまうと、エラーが出てそこでプログラムが止まってしまうからです。。。

つまり、共通メソッドごとにグループ分けをしていきます。

よく使用するのは、テキストボックス、ラジオボタン、チェックボックス、プルダウンあたりかと思いますので、

  • 配点が確認できるタイプ(getPoint()が使えるタイプ)
  • 選択肢があるタイプ(getChoices()が使えるタイプ)

現状、これらが判別できればよさそうです。

getChoices()できるのは、執筆現在で

  • ラジオボタン
  • チェックボックス
  • プルダウン

の3種類なので、それらのcaseではキャストのあとに

        hasChoice = 1;
を加えます。
さらに、配点可能なものは上記に加えて
  • テキストボックス系
  • 時間選択系

になります。

これらには、

hasPoint = 1;
を加えます。
※グリッドアイテムは、列ごとに配点可能ですが、執筆現在、Google Apps Scriptでは配点を設定・取得することができないようです。それ以外の回答用アイテムは配点を取得できます。
こんな感じです。
      case 'MULTIPLE_CHOICE':
        testPartsItem = itemparts.asMultipleChoiceItem();
        hasChoice = 1;
        hasPoint = 1;
        break;

こうしておくと、もしhasChoice = 1ならば選択肢取得の処理、もしhasPoint = 1ならば、配点取得の処理…という形で共通処理をまとめることができます。

さて、これで実行・・・!

なのですが、何か様子がおかしい・・・。

選択肢が全く取得されないのです。ラジオボタンだろうがチェックボックスだろうが、シートに書き込まれるのは質問内容とアイテムタイプのみ。

慌ててコードがどこまで動いているか、Logを追加してチェック。

まずは、hasChoice が1になっているかどうか、ログを追加してみると、結果はすべて「0」のまま。。。

つまり、このswich自体がうまく機能していない(どのcaseにも入っていない)ようなのです。

こんなとき、頼りになる(ようでならない)のがChatGPT。

ChatGPTに惑わされつつ、一緒にいろいろと原因を調べた結果、swichの条件は、ifでいうと

itemType === ‘MULTIPLE_CHOICE’ (イコールが3つついています;;;)

になるそう。ちなみに前回、ifで書いた条件式は

 if (itemType == 'MULTIPLE_CHOICE') {

なので、イコール2つです。イコールが3つになると、「データ型まで完全に一致した場合」ということになるらしいのです。ログを見てもどちらもMULTIPLE_CHOICEで同じに見えるんですけど、、、

試しにitemTypeのデータ型を調べるために

    Logger.log(typeof itemType); //ログを確認

としてみると結果は「object」

case ‘MULTIPLE_CHOICE’:という書き方だと、string型というテキストの型になるので、

どちらもMULTIPLE_CHOICEだけど、object ≠ stringなので、厳密には一致しないということらしいです。

なにそれ、細かい…😵

じゃあ、’MULTIPLE_CHOICE’をオブジェクト型で書き直します。

オブジェクト型の書き方は、itemType.MULTIPLE_CHOICEとなります。

switchの分岐を

      case itemType.MULTIPLE_CHOICE:
        testPartsItem = itemparts.asMultipleChoiceItem();
        hasChoice = 1;
        hasPoint = 1;
        break;

と書き換えると、ちゃんと動きました😂

素人が基礎をぶっ飛ばしてコードを書くと、しばしばこのような初歩的な間違いで足止めを食らいます。

Google Apps Scriptで初めてプログラムを書いてみようと思われるかたは、せめてJavaScriptの基本的な説明が書かれている書籍を手元に置くことをお勧めします。

書籍はたいてい分厚いですが、読むのが大変であれば躓いたときにだけでもページをめくってみてください。

ここまでのコード

function myFunction() {

  // フォームを開く
  const form = FormApp.openByUrl(フォームのURL);
  // URLは'https://docs.google.com/fo******/edit'のように''で囲んで入力

  //フォームの要素を配列で取得
  const items = form.getItems();

  //スプレッドシートを開く。
  const spsheet = SpreadsheetApp.openByUrl(シートのURL);
  // URLは'https://docs.google.com/fo******/edit'のように''で囲んで入力
  const sheet = spsheet.getActiveSheet();
  //処理を加えるシートとして、ファイルを開いて最初に表示される(アクティブな)シートを設定

  sheet.clear(); //シートの内容をクリア

  //シートに行を追加し、formタイトル入力
  sheet.appendRow(['フォームタイトル', form.getTitle()]);
  // 項目タイトル行を作成
  sheet.appendRow(['アイテムタイプ', '質問内容']);

  for (let i = 0; i < items.length; i++) {
    //Item数をiと置き換えて、iを0から順番に項目の数だけ1ずつ増やす
    const itemparts = items[i]; //itemという配列のi番目の要素をitempartsとして取り出す。
    const itemType = itemparts.getType(); //取得したitempartsのタイプを確認してitemTypeに保管
    const itemText = itemparts.getTitle();//取得したitempartsの質問内容を確認してitemTextに保管
    // sheet.appendRow([itemType, itemText]); // それぞれシートの新しい行に入力
    let itemSheetinput = [itemType, itemText]; // ↑入力ではなく一旦配列に格納

    let hasChoice = 0;
    let hasPoint = 0;
    Logger.log(typeof itemType); //ログを確認

    //swichによりそれぞれのアイテムにキャスト
    switch (itemType) {
      case itemType.CHECKBOX:
        testPartsItem = itemparts.asCheckboxItem();
        hasChoice = 1;
        hasPoint = 1;
        break;
      case itemType.CHECKBOX_GRID:
        testPartsItem = itemparts.asCheckboxGridItem();
        break;
      case itemType.DATE:
        testPartsItem = itemparts.asDateItem();
        hasPoint = 1;
        break;
      case itemType.DATETIME:
        testPartsItem = itemparts.asDateTimeItem();
        hasPoint = 1;
        break;
      case itemType.DURATION:
        testPartsItem = itemparts.asDurationItem();
        hasPoint = 1;
        break;
      case itemType.GRID:
        testPartsItem = itemparts.asGridItem();
        break;
      case itemType.IMAGE:
        testPartsItem = itemparts.asImageItem();
        break;
      case itemType.LIST:
        testPartsItem = itemparts.asListItem();
        hasChoice = 1;
        hasPoint = 1;
        break;
      case itemType.MULTIPLE_CHOICE:
        testPartsItem = itemparts.asMultipleChoiceItem();
        hasChoice = 1;
        hasPoint = 1;
        break;
      case itemType.PAGE_BREAK:
        testPartsItem = itemparts.asPageBreakItem();
        break;
      case itemType.PARAGRAPH_TEXT:
        testPartsItem = itemparts.asParagraphTextItem();
        hasPoint = 1;
        break;
      case itemType.SCALE:
        testPartsItem = itemparts.asScaleItem();
        hasPoint = 1;
        break;
      case itemType.SECTION_HEADER:
        testPartsItem = itemparts.asSectionHeaderItem();
        break;
      case itemType.TEXT:
        testPartsItem = itemparts.asTextItem();
        hasPoint = 1;
        break;
      case itemType.TIME:
        testPartsItem = itemparts.asTimeItem();
        hasPoint = 1;
        break;
      case itemType.VIDEO:
        testPartsItem = itemparts.asVideoItem();
        break;
    }
    Logger.log(hasChoice); //ログを確認

    //選択肢取得処理開始
    if (hasChoice == 1) {
      const choices = testPartsItem.getChoices(); //選択肢を取得
      const choicesText = choices.map(x => x.getValue()); //選択肢のテキストを取得
      itemSheetinput = itemSheetinput.concat(choicesText); //アイテムタイプや質問内容の配列と合体
      Logger.log(itemSheetinput); //ログを確認
    }
    sheet.appendRow(itemSheetinput); //まとめてシートの新しい行に入力
  }

}

コメント

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