どうも。つじけ(tsujikenzo)です。このシリーズでは「業務マニュアルとアセスメントシートを(同時に)作ろう」についてお送りしています。全7回の予定で、今日は第5回目です。
前回のおさらい
前回はドキュメントから2次元配列用の配列を作成し、ドキュメントからスプレッドシートを新規作成しました。
まだ中身は空っぽなので、今回は「2次元配列の流し込み」と「No列の作成」を行います。
作業に入る前に、私は「マイドライブへの保存」が嫌なので💦、指定したフォルダに移動するコードを加えておきます。
フォルダ移動
前回、スプレッドシートを新規作成した際に、スプレッドシートオブジェクトを変数[ss]に、スプレッドシートIDを変数[SSID]に格納していました。
function createSS(title){ const ss = SpreadsheetApp.create(title); const SSID = ss.getId(); }
DriveAppクラスの.getFIleByID(id)メソッドはファイルオブジェクトを取得することができます。
const file = DriveApp.getFileById(SSID);
スプレッドシートを保存したいフォルダIDをメモしておきます。(IDの確認方法は割愛させていただきます)
Drive Appクラスの.getFolderById(id)メソッドはフォルダオブジェクトを取得することができます。
const folderID = 'フォルダID'; const folder = DriveApp.getFolderById(folderID);
DriveサービスのFileクラスの.moveTo(destination)メソッドは[destination]へファイルを移動させることができます。[destination]にはFolderオブジェクトを指定します。
file.moveTo(folder);
まとめるとこのようなコードになります。後で実行しますので、コードだけ追加しておきましょう。
const ss = SpreadsheetApp.create(title); const SSID = ss.getId(); const file = DriveApp.getFileById(SSID); const folderID = 'フォルダID'; const folder = DriveApp.getFolderById(folderID); file.moveTo(folder);
2次元配列の貼り付け
さて、スプレッドシートに貼り付ける2次元配列は、関数documentConverter()の戻り値 [title, array]に格納されていました。
function documentConverter(id){ ~中略~ return [title, array]; }
この戻り値を使って、関数createAssessmentSheet()でスプレッドシートを新規作成します。
function createAssessmentSheet(){ const array = documentConverter('作成したマニュアルのドキュメントID'); createSS(array[0], array[1]); } function createSS(title, array){ const ss = SpreadsheetApp.create(title); const SSID = ss.getId(); const file = DriveApp.getFileById(SSID); const folderID = 'フォルダID'; const folder = DriveApp.getFolderById(folderID); file.moveTo(folder); ss.getActiveSheet().getRange(1,1,array.length, array[0].length).setValues(array); }
関数createAssessmentSheet()を実行すると、スプレッドシートを作成できています👏
さらに、指定したフォルダにスプレッドシートが作成されています。
No列
Noを更新する処理は少し複雑ですので、別途パーツ化します。
function名はsetParagraphNumbers()としましょう。書く場所は同じスタンドアロンスクリプトの最終行以降で構いません。
function setParagraphNumbers(){ // 処理 }スプレッドシート全体を2次元配列に取得します。(後でtitlesに再代入しますので、let宣言しています。)
const ss = SpreadsheetApp.getActiveSheet(); let titles = ss.getDataRange().getValues();
空の[配列]、[カウント変数]、[flag]を準備します。
//empty array const addNums = []; //count var let countHeading2 = 0; let countHeading3 = 0; let countHeading4 = 0; //def flag let flag1 = false; let flag2 = false; let flag3 = false; let flag4 = false;変数[titles]をfor文で回しながら、配列にpushしていきます。現在地を判定しながらNo.を付与していくイメージです。
for (const title of titles) { //set a flag on switch (title[0]) { case 'TITLE': flag1 = true; break; case 'HEADING2': if (flag2 === false && flag3 === false && flag4 === false) flag2 = true; if (flag2 && flag3 && flag4 === false) { countHeading3 = 0; flag3 = false; } if (flag2 && flag3 && flag4) { countHeading3 = 0; countHeading4 = 0; flag3 = false; flag4 = false; } break; case 'HEADING3': if (flag2 && flag3 && flag4) { countHeading4 = 0; flag4 = false; } flag3 = true; break; case 'HEADING4': flag4 = true; break; } if (flag1) addNums.push(['1']); flag1 = false; //append nums if (flag2 && flag3 === false && flag4 === false) { countHeading2++; addNums.push([`1.${countHeading2}`]); } if (flag2 && flag3 && flag4 === false) { countHeading3++; addNums.push([`1.${countHeading2}.${countHeading3}`]); } if (flag2 && flag3 && flag4 === true) { countHeading4++; addNums.push([`1.${countHeading2}.${countHeading3}.${countHeading4}`]); } }
配列[addNums]をログで確認するとこのような結果になります。
最後に.map()メソッドで整形します。
titles.shift(); titles = titles.map((element, index) => [element[0], addNums[index][0], element[2]]); titles.unshift(['Style', 'No', 'Heading']);
変数[titles]にはこのような2次元配列が格納されています。
関数setParagraphNumbers()の戻り値として[titles]を設定します。
return titles
関数createSS()の最下部で、再度スプレッドシートに2次元配列を貼り付けます。
function createSS(title, array){ ~中略~ ss.getActiveSheet().getRange(1,1,array.length, array[0].length).setValues(array); //[No]列を更新する array = setParagraphNumbers_(SSID,sheetName); ss.getActiveSheet().getRange(1,1,array.length, array[0].length).setValues(array); }`
コードのパーツ化
最後に今まで書いたコードをまとめます。コードの中にマジックナンバー(直書き)になってしまっている値などの関数の外に出したり、プライベート関数化_を施したコードはこちらです。
/** * ドキュメントから新規スプレッドシートを作成する * * @param {string} ドキュメントID * @param {string} スプレッドシートの保存先フォルダID*/ function createAssessmentSheet(docId, folderId){ //引数が無い時は下記2行をコメントインしてください //docId = 'マニュアルのドキュメントID'; //folderId = '保存先のドライブID'; const array = documentConverter_(docId); cosnt ss = createSS_(array[0], array[1], folderId) } /** * ドキュメントIDを渡すと2次元配列を返す * * @param {string} ドキュメントトID * @return {array} [title,2次元配列, */ function documentConverter_(id){ const doc = DocumentApp.openById(id); const title = doc.getName(); const array = [['Style', 'No', 'Heading'],['TITLE','',title]]; const paragraphs = doc.getBody().getParagraphs(); for (const parag of paragraphs) { const style = parag.getHeading().toJSON(); switch (style) { case 'HEADING2': array.push([style, '', parag.getText()]); break; case 'HEADING3': array.push([style, '', parag.getText()]); break; case 'HEADING4': array.push([style, '', parag.getText()]); break; default: } } return [title, array]; } /** * ドキュメントを変換した2次元配列から新規スプレッドシートを作成する * * @param {string} ドキュメントのタイトル * @param {array} ドキュメントの2次元配列 * @param {string} 保存先のフォルダID
* @return {object} 新規作成したスプレッドシートオブジェクト */ function createSS_(title, array, folderId){ const ss = SpreadsheetApp.create(title); const SSID = ss.getId(); const file = DriveApp.getFileById(SSID); const folder = DriveApp.getFolderById(folderId); file.moveTo(folder); ss.getActiveSheet().getRange(1,1,array.length, array[0].length).setValues(array); //[No]列を補完する array = setParagraphNumbers_(SSID); ss.getActiveSheet().getRange(1,1,array.length, array[0].length).setValues(array);
return ss; } /** * スプレッドシートオブジェクトを渡すとNoを補完した2次元配列を返す * * @param {object} スプレッドシート * @return {array} Noを補完した2次元配列 */ function setParagraphNumbers_(ss) { let titles = ss.getActiveSheet().getDataRange().getValues();
//empty array const addNums = []; //count var let countHeading2 = 0; let countHeading3 = 0; let countHeading4 = 0; //def flag let flag1 = false; let flag2 = false; let flag3 = false; let flag4 = false; for (const title of titles) { //set a flag on switch (title[0]) { case 'TITLE': flag1 = true; break; case 'HEADING2': if (flag2 === false && flag3 === false && flag4 === false) flag2 = true; if (flag2 && flag3 && flag4 === false) { countHeading3 = 0; flag3 = false; } if (flag2 && flag3 && flag4) { countHeading3 = 0; countHeading4 = 0; flag3 = false; flag4 = false; } break; case 'HEADING3': if (flag2 && flag3 && flag4) { countHeading4 = 0; flag4 = false; } flag3 = true; break; case 'HEADING4': flag4 = true; break; } if (flag1) addNums.push(['1']); flag1 = false; //append nums if (flag2 && flag3 === false && flag4 === false) { countHeading2++; addNums.push([`1.${countHeading2}`]); } if (flag2 && flag3 && flag4 === false) { countHeading3++; addNums.push([`1.${countHeading2}.${countHeading3}`]); } if (flag2 && flag3 && flag4 === true) { countHeading4++; addNums.push([`1.${countHeading2}.${countHeading3}.${countHeading4}`]); } } titles.shift(); titles = titles.map((element, index) => [element[0], addNums[index][0], element[2]]); titles.unshift(['Style', 'No', 'Heading']); return titles; }
ドキュメントIDとフォルダIDをコード内に入力して、createAssessmentSheet()を実行すると、このようなスプレッドシートが作成されます。
まとめ
さて、前半は[フォルダ移動]機能を付け加えて、ドキュメントから2次元配列を流し込みしましたが、[No列]の更新は少々複雑でしたので関数を分けて行いました。
1つのコードで様々な処理をしてしまうと、後々メンテナンスが大変です💦し、便利なコードを後で使いまわしたい時に、関数を切り離していた方が便利です。ここは要件定義と重なってくる非常に難しい分野ですが、コードのパーツ化に関するテクニックは別途ブログを書こうと思います。
次回は「セルの書式設定」についてお届けします。お楽しみに!