質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

Q&A

解決済

3回答

2239閲覧

スプレッドシート内で数式が含まれている複数のセルに対して一括で保護をかけたい

asutaro

総合スコア1

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

0グッド

1クリップ

投稿2021/09/18 14:00

前提・実現したいこと

数式が組まれている複数のセルに対して一括で保護をかけたい

発生している問題・エラーメッセージ

対象のセルが多く6分間を超過してしまい処理が途中でキャンセルされる

エラーメッセージ

該当のソースコード

// アクティブなシート内の数式セルを保護 function protectActiveSheetFormulas() { const sheet = SpreadsheetApp.getActiveSheet(); console.log(sheet.getName()); protectFormulaCells(sheet); } // 全てのシートの数式セルを保護 function protecAllSheetsFormulas() { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheets = ss.getSheets(); for (const sheet of sheets) { console.log(sheet.getName()); protectFormulaCells(sheet); } } // 数式が含まれるセルを取得して保護していく関数 function protectFormulaCells(sheet) { //全データ範囲を取得 const allRange = sheet.getDataRange(); //全データ範囲の数式を取得 const formulas = allRange.getFormulas(); //データ配列数=行数を取得 const numFormulas = formulas.length; // console.log(numFormulas); //データ範囲の列数を取得 const lastColumn = sheet.getLastColumn(); // console.log(lastColumn); // 数式を含むセルの範囲を一つずつ取得し保護 for (let i = 0; i < numFormulas; i++) { for (let j = 0; j < lastColumn; j++) { const cell = formulas[i][j]; if (cell.length > 0) { // console.log(`${i + 1}:${j + 1}`); console.log(sheet.getRange(i + 1, j + 1).getA1Notation()); sheet.getRange(i + 1, j + 1).protect().setDescription('数式セルの為保護'); } } } }

試したこと

他の方を参考に6分間超過する際の対処法を組み込みましたが上手くできず

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

GASでセルに対する処理をするときには、「できる限りまとめて読み書きの処理をする」必要があります。
今回ですと、getFormulasで数式の読み込みはまとめて行なっていますが、protectの処理を1セルごとに行なっているため、処理時間がオーバーしてしまいます。

そこで、「列方向に連続している数式の範囲をまとめてprotectする」ことを考えます。たいていの場合、スプレッドシートでは列方向に同種のデータを並べますからね。

GAS

1function myFunction() { 2 const sheet = SpreadsheetApp.getActiveSheet(); 3 4 const data_range = sheet.getDataRange(); 5 const start_row = data_range.getRow(); 6 const start_col = data_range.getColumn(); 7 console.log(data_range.getA1Notation()) 8 const formulas = data_range.getFormulas(); 9 const rects = []; 10 11 // 列方向に連続した数式があればまとめて範囲にする 12 for (let x = 0; x < formulas[0].length; x++) { 13 // 数式か否かの真偽値のインデックス付きリストを作り、真偽値の切り替わる部分だけ残す 14 let packs = formulas.map((f, i) => ({value:!!f[x], index:i})).filter((t, i, arr) => (i === 0 || arr[i - 1].value != t.value)); 15 if (packs.slice(-1)[0].value) { 16 // 末尾の要素がtrueならfalseの要素を追加 17 packs.push({value:false, index:formulas.length}) 18 } 19 if (packs[0].value === false) { 20 // 先頭の要素がfalseなら削除する 21 packs = packs.slice(1); 22 } 23 24 for (let i = 0; i < packs.length; i += 2) { 25 rects.push({row:packs[i].index + start_row, 26 col:x + start_col, 27 num_rows:packs[i + 1].index - packs[i].index, 28 num_cols:1}) 29 } 30 // console.log(`x: ${x}`) 31 // console.log(rects) 32 } 33 34 // rectに対する行方向の統合(めんどくさいのでパス) 35 36 for (let rect of rects) { 37 const target_range = sheet.getRange(rect.row, rect.col, rect.num_rows, rect.num_cols); 38 const protection = target_range.protect().setDescription("数式セルの保護"); 39 protection.removeEditors(protection.getEditors()); 40 } 41}

これで各列ごとに、数式が連続している部分はひとつの範囲として保護されるので、時間オーバーにはなりにくいと思います。

投稿2021/09/19 04:44

編集2021/09/19 06:26
Daregada

総合スコア11990

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

asutaro

2021/09/21 01:27

ご丁寧に回答頂きありがとうございます。 実は連続した数式ではないのですが、その場合の解決策などありますでしょうか・・・ 数式という括りが難しいようであれば「空白以外のセルを保護」という策も考えております。
Daregada

2021/09/21 01:39 編集

この回答のコードは、「列ごとに数式が連続している範囲があればそれをひとつにまとめた範囲にする」もので、連続していなくてもそのまま使えます。 たとえば、B1:B50の範囲にいくつか数式でないセルが含まれていても、(B1が数式でなければ範囲に入れずに)「B2:B10を保護」、(B11が数式でなければ範囲に入れずに)「B12:B15を保護」、(B15:B20が数式でなければ範囲にいれずに)「B21:B50を保護」という動作をします。
asutaro

2021/09/21 09:34

ありがとうございます! 保護無事掛かっており解決致しました。
guest

0

なんか使う機会ありそうだったんで、私も備忘録的にコード書いたのを回答しますね。

方向性としてはDaregadaさんの回答されてる通りで、単体セルを一つ一つprotectする処理が遅い原因と思われるので、手動でやる時と同じように数式が入ってるセルが連続してるならセル範囲の記述に変換して、最後にprotectという手順が良いかと。

取得した配列をコネコネするのは、とんでもないデータ量じゃなければ遅延の原因にはなりにくいので、こんな感じのコードで試してみてはどうでしょうか。

GAS

1function protectFormulaCells2(){ 2 const sheet = SpreadsheetApp.getActiveSheet(); 3 const range = sheet.getDataRange(); //A列、1行目がなにかしら入力されている想定 4 const formulas = range.getFormulas(); 5 6 //数式が入ったセル位置情報を二次元配列化 7 const formulasRangeList = formulas.flatMap((r,i) => r.map((f,j) => f? [i+1,j+1,1,1] : "")).filter(v=>v); 8 9 //最初に列方向(縦に結合) 10 11 let result =[]; 12 13 for(col=1; col<=formulas[0].length; col++){ 14 const targetColArr = formulasRangeList.filter(range => range[1]==col); 15 if(!targetColArr.length) continue; //列に数式のあるセルが無い場合は次へ 16 17 const array =targetColArr.reduce((acc,cv)=>{ 18 if(acc[0]+acc[2]==cv[0]){ 19 acc[2]+=1; 20 return acc 21 }else{ 22 result.push(acc); 23 return cv 24 }; 25 }); 26 result.push(array); 27 }; 28 29 console.log(result); //列方向結合の結果 30 31 //さらに行方向(横に結合) 32 result = result.reduce((acc,cv)=>{ 33 //開始行と行数(高さ)が同じで、隣同士のセル範囲をまとめていく 34 const index = acc.findIndex(arr => arr[0]==cv[0] && arr[1]+arr[3]==cv[1] && arr[2]==cv[2]); 35 if(index > -1){ 36 acc[index][3]+=1; 37 return acc; 38 }else{ 39 acc.push(cv); 40 return acc; 41 } 42 },[]); 43 44 console.log(result); //さらに行方向に結合した結果 45 46 47 result.forEach(range =>{ 48 sheet.getRange(...range).protect().setDescription('数式セルの為保護'); 49 }); 50 51}

投稿2021/09/19 13:01

sawa

総合スコア3002

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

事前に保護された範囲を取得し、保護されていない範囲をprotect()したらどうでしょうか?
保護された範囲は以下のコードのように取得できるようです。保護されている範囲が
formula
だった場合にA1Notationなんかを取得して配列に保存し、indexOfincludesを使い、ゴニョゴニョしてみたらどうでしょうか?

GAS

1function myFunction() { 2 const ss = SpreadsheetApp.getActive(); 3 const protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE); 4 for (var i = 0; i < protections.length; i++) { 5 const protection = protections[i]; 6 const protection_range = protection.getRange(); 7 console.log(protection_range.getA1Notation()); 8 console.log(protection_range.getFormula()); 9 } 10} 11

投稿2021/09/19 11:11

fake_shibe

総合スコア806

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問