GASでフォームを量産する方法。

# GoogleFormsを一度にまとめてつくりたいとき～。
たとえばこんなケース。

なんらかの集まりで個人ごとの集計取りたいけど、メールアドレスの収集が難しくて回答を個人で分けられない？

これが付けられないケース。PTAとかで「え？メールアドレス？わかんない覚えてない」なことは多々あります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167883/2c0afd7a-6627-7a66-d8f0-3e52eef81b24.png)


しかもまぁ、けっこうみんな入力ミスる。自分のメアド、あんまり書かないから表記ゆれも頻発するんですよね。

# だったら一人一人専用のフォームを作ればいいじゃない。

うん、解決。
デモ手作業で同じフォームたくさん作って管理するの死ぬほど面倒くさいからタスケテGAS！

# 実装の時間です！

つける機能はまずこちら！

1. スプレッドシートでマスタを作ってフォームを一括生産。
1. 作ったフォームは指定のフォルダに移動。
1. フォームのファイル名を個別に自動付与。
1. 回答項目も動的管理を見据えてスプレッドシートでマスタ管理。
1. フォームと、マスタのスプレッドシートを自動的に連携、シート名をフォームの回答1からフォームのファイル名に自動変換。
1. フォームの回答シートをGASでまとめて1枚のシートに集約。


何回かに分けて段階的に解説していきます。


## プログラムの動きは以下の感じ。

1. 保存フォルダの中のファイルを走査してリスト化する。
1. マスタシートと差分を確認して、新規作成するファイルを認識させる。
1. マスタシートに入力された回答項目・ファイル名のフォームを作成する。
1. 保存フォルダに移動させる。
1. フォームをマスタシートと連携させてシート名をフォームのファイル名+の回答にする。
1. もう一度保存フォルダ内を走査してリストを更新する。
1. マスタシートに仕込んだArayformulaとvlookup関数がマスタシートにファイルリストの情報を取ってくれる。

こんな感じ。
ファイル操作はGASで、スプレッドシート内のマッチングはArrayformulaとvlookupでお手軽実装します。

スプレッドシートをこんな感じに編集してください。

使うシートはこの二つ。
パラメーターの動的管理はについては書くと長くなるのでひとまず置いておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167883/fed9029a-f585-11e1-3456-5480bf94f56c.png)

formMasterはこんな感じで必要項目を入れておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167883/a20b1e0e-a084-aae9-edda-aa3c4d905df7.png)

H,I,K,L列にはArrayformula関数を仕込んでます。
ここがラクチンのミソです。
種明かしはまた明日以降に・・・。


# じゃ、コードね。

```Javascript
function editForm(){

  getFileList();
  createForm();
  getFileList();

}

function createForm() { 

  const ss = SpreadsheetApp.getActiveSpreadsheet();
  //このシートでコントロールするフォームマスターのリストシートをゲット
  const shMaster = ss.getSheetByName("formMaster");


  //マスターシートの全データを取得してヘッダーを外す。
  const masterList = shMaster.getDataRange().getDisplayValues();
  masterList.shift();
  
  //ラジオボタンアイテムの名称を取得する。
  const shItem = ss.getSheetByName('shItem');
  const choiceItem = shItem.getDataRange().getDisplayValues();

  //フォームのファイル名を重複削除して取得。
  const fileNameList = masterList.map(x => x[0]);
  const fileName = Array.from(new Set(fileNameList)).filter(x=> x !== "");
  //fileNameを1次配列化する。

  //指定フォルダの中のフォームファイルリストを取得する。
  const forderId = "フォルダのid"
  const folder = DriveApp.getFolderById(forderId);
  
  const fileList = folder.getFiles();
  const nameAndUrlList = [];

  while(fileList.hasNext()){
    const buff = fileList.next();
    nameAndUrlList.push([buff.getName(),buff.getUrl(),buff.getMimeType()]);
    }
  
  const formList_ = nameAndUrlList.filter(x => x[2] == "application/vnd.google-apps.form");
  const formList = formList_.map(x=>x[0]);
  const sabun = fileName.filter(x => formList.indexOf(x) == -1);

  if(sabun.length !== 0){

  for(i=0;i<sabun.length;i++){
    
    const form = FormApp.create(sabun[i].toString());

    //フォーム名をセット。フォームのファイルIDを取得。
    form.setTitle(sabun[i]).setDescription("フォーム製造サンプル");
    const fileId = form.getId();

    //セットするアイテムのリストを取得して配列変数化。
    console.log(masterList);
    const setItemLists = masterList.filter(x => x[0] == sabun[i]).map(x => x[6]);
    console.log(setItemLists);

    //フォームのURLを取得。
    const fileURL = form.getEditUrl();

    const file = DriveApp.getFileById(fileId);
    file.moveTo(folder);

    //マスタリストをもとに、アイテム名を追加して名前をセット。
    for(j=0;j<setItemLists.length;j++){
      //散布日の文字列が入っていれば、日付アイテムをセットする。
      if(setItemLists[j].indexOf("実施日") !== -1){
        form.addDateItem().setTitle(setItemLists[j]);
        //ラジオボタンの文字が入っていれば、ラジオボタンのタイトルにセットする。
      }else if(setItemLists[j].indexOf("種類") !== -1){
        form.addMultipleChoiceItem().setTitle(setItemLists[j]).setChoiceValues(choiceItem);
        }else{;}      
      
    }
  }


  }else{;}

  

}

function getFileList() {

   //指定フォルダの中のフォームファイルリストを取得する。
  const forderId = "フォルダのid"
  const folder = DriveApp.getFolderById(forderId);

  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const shFileList = ss.getSheetByName("FileList")
  shFileList.clear();
  
  const fileList = folder.getFiles();
  const nameAndUrlList = [];

  while(fileList.hasNext()){
    const buff = fileList.next();
    nameAndUrlList.push([buff.getName(),buff.getUrl(),buff.getMimeType()]);
  }

  const formFiles = nameAndUrlList.filter(mime => mime[2] == "application/vnd.google-apps.form");
  console.log(formFiles)
  formFiles.map(x =>x.pop());

  const setValues = formFiles.map(x => getItemInfo(x)).flat();
  console.log(setValues);
  const row = setValues.length;
  const col = setValues[0].length;
  const range = shFileList.getRange(1,1,row,col);
  range.setValues(setValues);


  function getItemInfo([filename,url]){
  const form = FormApp.openByUrl(url)
  const formItems = form.getItems();
  const formItemList = [];
  for(let j=0;j<formItems.length;j++){
    const fi = formItems[j];
    formItemList.push([filename,fi.getTitle(),fi.getIndex(),fi.getId(),fi.getType(),url]);    
    
  }
  return formItemList;

  }
  
}
```


指定したフォルダに、ヤマネコとウミネコのFormが出来ました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167883/2809b392-86e7-7306-6966-7482a6c497a9.png)

一気に解説するのも大変なので解説はまた遂行に。

そんじゃばいびー。