function
1var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); 2var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート9'); 3var rangeList = sheet.getActiveRangeList().getRanges(); 4for(var i = 0; i < rangeList.length ; i++){ 5 6 7sheet.getRange(i+150, 1).setValue(`= 8query('シート9'!A2:U,"where (D='"&indirect(address(${rangeList[i].getRow()-1+1},3))&"'or L='"&indirect(address(${rangeList[i].getRow()-1+1},3))&"') 9and year(A)="&text(substitute(substitute(indirect(address(1,${rangeList[i].getColumn()-1})),"-",""),",","-"),"YYYY")&" and month(A)+1="&text(substitute(substitute(indirect(address(1,${rangeList[i].getColumn()-1})),"-",""),",","-"),"MM")&"")) 10)`); 11 12}} 13コード
質問を改めます。
元データがありピボットデーブルを作成したのち、テーブルの一つのセルをダブルクリックすれば元データの該当部分が展開されますが、テーブルの複数セルを選択して同時に展開するものをシート9に作りたいです。上記コードは途上のものですが、まずは、アクティブで複数選択したセルのテーブルの行と列の内容を取得することを考えています。(今はテーブルがc列と1行に枠として展開されています。)
sheet.getRange(i+1, 1).setFormula(=indirect(address(rangeList[i].getRow()-1+1,3))
);
sheet.getRange(i+1, 2).setValue(=indirect(address(1,rangeList[i].getColumn()-1))
);
の部分でiが回っていないのですが、setformulaがA1形式のみ対応らしいのですが、これをiで読み取れるようにするにはどうすればよろしいでしょうか?
どうぞよろしくお願いいたします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答3件
0
ベストアンサー
質問者さんのコードやコメントから、「おそらくこういう動作がしたいのだろう」というのを推測して作ったコードが下記になります。(後半の「テストデータ前提」をご参照ください)
(注意)
・質問者さんが理解しやすいと思うので、QUERYを含む数式をそのままシート9に転記する方式としています。
(GAS内で組み立てる方式ではない)
QUERY関数そのものはスプレッドシート上の数式でしか利用できません。
現状のQUERYを使った数式と同じ動作をGASで実装することも可能ですが、この場合は、まず「どのような動作にしたいのか」をしっかり固める必要があると思います。
QUERYを使った「数式」を汎用的に「GASのコード」に変える手段はおそらく存在せず、結局「やりたい動作」を行うために個別にカスタマイズせざるを得ません。
数式を使っていれば、質問者さんの方でカスタマイズも容易であると考えました。
・mapやfilter等を駆使すればもっと短く・速くできるところはたくさんあるかもしれませんが、上記と同様の理由で、あえて冗長であったり、速度を犠牲にした書き方をしている部分があります。
(元のmyFunctionXY()の中では、constやletを使った書き方も使用していません)
・具体的な動作が読解し切れていないので、期待する動作と異なる部分があるかもしれませんが、御了承ください。
(動作検証環境:Windows 10 /Chromium Edge/GAS:V8エンジン有効)
シート9の展開先開始行を変えたい場合は、コード中のOUTPUT_START
の数字を変えて保存・実行してください。
js
1function myFunctionXY() { 2 3 var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); 4 var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート9'); 5 6 // 展開先の行番号(例:150目以降に展開したい場合「150」を指定) 7 var OUTPUT_START = 150; 8 9 // 展開先に入っているデータを削除 10 sheet2.getRange("A" + OUTPUT_START + ":L1000").clearContent(); 11 12 // アクティブな範囲の配列を取得 13 var rangeList = getSprittedRange(sheet.getActiveRangeList()); 14 15 // 展開先の最終行を格納する。 16 var lastRow = OUTPUT_START; 17 18 // 結果が見つかったかどうか 19 var isFound = false; 20 21 // 取得したアクティブな範囲をループで回す 22 for (var i = 0; i < rangeList.length; i++) { 23 // 出力先行番号の設定。書き込み済みの行から1行空けて始める。 24 lastRow = sheet2.getLastRow() + 2; 25 // 最初だけはOUTPUT_START行目に設定 26 if (lastRow <= OUTPUT_START + 1) lastRow = OUTPUT_START; 27 28 sheet2.getRange(lastRow, 1).setValue( 29 `=query('シート9'!A2:U${OUTPUT_START - 1},` 30 + `"where (D='"&${getAddress(sheet, rangeList[i].getRow() - 1 + 1, 3)}&"'` 31 + ` or L='"&${getAddress(sheet, rangeList[i].getRow() - 1 + 1, 3)}&"')` 32 + ` and year(A)="&text(substitute(substitute(${getAddress(sheet, 1, rangeList[i].getColumn() - 1)},"-",""),",","-"),"YYYY")&"` 33 + ` and month(A)+1="&text(substitute(substitute(${getAddress(sheet, 1, rangeList[i].getColumn() - 1)},"-",""),",","-"),"MM")&"")`); 34 35 // 結果が見つからない場合は消す。 36 if (sheet2.getRange(lastRow, 1).getValue() === "#N/A" || 37 sheet2.getRange(lastRow, 1).getValue() === "#VALUE!") { 38 sheet2.getRange(lastRow, 1).clearContent() 39 } else { 40 // 結果が見つかった場合はフラグをtrueにしておく 41 isFound = true; 42 } 43 } 44 45 if (!isFound) { 46 sheet2.getRange(lastRow, 1).setValue("見つかりませんでした"); 47 } 48} 49 50/*************************************************** 51* 指定したシート・行・列の「A1」形式のセル番地を返す。 52* 53* 引数: 54* sheetObj : Sheet : シートオブジェクト 55* row : Number : 行番号(1始まり) 56* col : Number : 列番号(1始まり) 57* 58* 戻り値: 59* string : 「'シート名'!「A1」形式セル番地」という形式の文字列 60* 例: 'シート1'!A10 61* (シート名の両側にはシングルクォーテーションが入ります) 62*****************************************************/ 63function getAddress(sheetObj, row, col) { 64 if (sheetObj == null) { 65 throw Error("引数 sheetObjが指定されていません。"); 66 } 67 68 if (row < 1) { 69 throw Error("行番号は1以上の数字でなければなりません。与えられた行番号(row)= " + row); 70 } 71 72 if (col < 1) { 73 throw Error("列番号は1以上の数字でなければなりません。与えられた列番号(col)= " + col); 74 } 75 76 return `'${sheetObj.getName()}'!${sheetObj.getRange(row, col).getA1Notation()}`; 77} 78 79 80 81/*************************************************** 82* 選択されているRangeオブジェクトが連続している場合、分割したセル範囲を返す。 83* 84* 引数: 85* rangelist : RangeList : 86* getActiveRangeList()で取得した、アクティブセルのRangeList 87* 88* 戻り値: 89* Range[] : 分割後の個別セル範囲(Range)を格納した配列 90*****************************************************/ 91function getSprittedRange(rangelist) { 92 // 戻り値(個別セル範囲)を格納するための配列 93 const output = []; 94 for (const range of rangelist.getRanges()) { 95 // RangeList が連続したセルの場合は、個別セル範囲に分割してoutput配列に追加 96 if (range.getHeight() != 1 || range.getWidth() != 1) { 97 for (let i = 1; i <= range.getHeight(); i++) { 98 for (let j = 1; j <= range.getWidth(); j++) { 99 output.push(range.getCell(i, j)); 100 } 101 } 102 } else { 103 output.push(range); 104 } 105 } 106 return output; 107} 108
上記のコードの検証に当たっては、手元で下記のようなテストデータをつくり、動作することを確認しています。
テストデータ前提
1つのスプレッドシートに、「シート1」「シート9」の2つのシートがあり、それぞれ画像の通りの内容となっています。
シート9
・シート1、シート9とも、A列の書式は「日付型」に設定しています。
・シート1には画像描画で作成した「実行」ボタンを配置しており、「スクリプトの割り当て」によって、クリックすると「myFunctionXY」関数が実行されるように設定しています。
動作について
シート1のデータ範囲のセルを1個以上クリックした状態で、実行ボタンをクリックすると、下記の動作を行います。
(1)シート1 のアクティブなセルの行の C列 にあるID(「A***
」)を読み取ります。
(2)シート1 のアクティブなセルの1つ前の列の1行目にある年月値(「2021,***
」)を読み取ります。
(3)シート9 の A1~L149 の範囲から、下記の条件にあてはまる行を抽出する数式を、シート9の150行目以降に入力します。
text
1抽出条件 2 ・シート9の D列 または L列が (1) で取得したIDと一致する 3 かつ 4 ・シート9の A列 の日付の年・月が、(2) で取得した年月値と一致する
(4)クリックしたセルが複数ある場合は、残りのセルに対して同じ動作を順次繰り返します。
それぞれのセルについて検索結果が見つかった場合は、シート9の既存の結果行から1行空けて数式を追記します。
例
たとえば、シート1 の F12セル をアクティブにした状態で、実行ボタンをクリックすると、
・アクティブセルのC列は「AAA12
」
・アクティブセルの1つ前の列の1行目は「2021.7
」
なので、
シート9の中から、
「D列またはL列がAAA12
である行のうち、A列の年月が 2021年7月
である行]
を探します。
そうすると、該当するのは13行目なので、シート9の13行目のデータが シート9の150行目に展開されます。
複数の結果がある場合は1行ずつ空けて表示します。(下記はサンプルです)
補足
・別シートのセル値取得
スプレッドシート上の数式で、同じスプレッドシート上の別シートのセル値を読み取る場合、indirect関数を使用する必要はありません。
単純に'シート名'!セル番地
とすれば指定したシートの指定した番地の値を取得できます。
・GAS内で、指定した行・列から、A1セル形式に変更する手段
getA1Notation() という関数を使うことで、A1セル形式の文字列に変換できます。
var ss = SpreadsheetApp.getActiveSpreadsheet(); var sheet = ss.getSheets()[0]; var range = sheet.getRange(2, 5); Logger.log(range.getA1Notation()); // "E2"
上記コード内のgetAddress()関数は、これを拡張し、
シートオブジェクト・行番号・列番号を与えると、対応する「シート名!A1」形式の文字列を返す関数としています。
・隣接するセルを選択したときのセル分割
Googleスプレッドシートの仕様として、隣接するセルをControlクリックで複数選択すると、1つのセルとして扱われてしまうことがあります。(少なくとも Windows10上のChromium Edgeではそうなっています)
上記コードのgetSprittedRange()関数は、これを個別のセルに分割する関数です。
function getSprittedRange(rangelist) { // 戻り値(個別セル範囲)を格納するための配列 const output = []; for (const range of rangelist.getRanges()) { // RangeList が連続したセルの場合は、個別セル範囲に分割してoutput配列に追加 if (range.getHeight() != 1 || range.getWidth() != 1) { for (let i = 1; i <= range.getHeight(); i++) { for (let j = 1; j <= range.getWidth(); j++) { output.push(range.getCell(i, j)); } } } else { output.push(range); } } return output; }
コメントでの追加質問に対する回答
、上記のシート1に相当するピボットテーブルのシートが複数あり、その際switch case break構文を使えば良いと教えていただいたのですが、上記コードにこれを組み込むにはどうすればよろしいでしょうか?(仮にシート2として表の位置は1行目D列に枠があると仮定していただけますと幸いです、これで私の思うものが完成する次第です)
シートごとに処理を変える場合の例です
(共通処理として出せる部分は多々あるかと思いますが、「各シートの処理で具体的にどのような差異があるのか」について全く明記されていない以上、こちらでは回答できかねます。
下記は共通処理も重複して書くことで、最低限動くであろうという例となります)
// メニュー追加処理 function onOpen() { var ui = SpreadsheetApp.getUi(); var menu = ui.createMenu('script'); menu.addItem('仕訳呼出', 'onClickMenu'); menu.addToUi(); } // メニューをクリックしたときに実行 function onClickMenu() { var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); switch (sheet.getName()){ case "シート1": myFunctionSheet1(sheet) break; case "シート2": myFunctionSheet2(sheet) break; case "シート3": myFunctionSheet3(sheet) break; default: //上記以外の場合は何もしない return; } // 以下に各シート共通処理を書く。 // .... } // シート1の処理コード function myFunctionSheet1(sheet) { var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート9'); // 展開先の行番号(例:150目以降に展開したい場合「150」を指定) var OUTPUT_START = 150; .... ~~以下略~~(シート1専用の処理)(中身は上記のコード中のmyFunctionXYを参考に書けばよい) .... } // シート2のコード function myFunctionSheet2(sheet) { var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート9'); // 展開先の行番号(例:150目以降に展開したい場合「150」を指定) var OUTPUT_START = 150; .... ~~略~~(シート2専用の処理) //「1行目D列に枠がある」->where節の読み取り列を3から4に変えればよい(?) sheet2.getRange(lastRow, 1).setValue( `=query('シート9'!A2:U${OUTPUT_START - 1},` + `"where (D='"&${getAddress(sheet, rangeList[i].getRow() - 1 + 1, 4)}&"'` + ` or L='"&${getAddress(sheet, rangeList[i].getRow() - 1 + 1, 4)}&"')` + ` and year(A)="&text(substitute(substitute(${getAddress(sheet, 1, rangeList[i].getColumn() - 1)},"-",""),",","-"),"YYYY")&"` + ` and month(A)+1="&text(substitute(substitute(${getAddress(sheet, 1, rangeList[i].getColumn() - 1)},"-",""),",","-"),"MM")&"")`); ~~以下略~~ } } // シート3のコード function myFunctionSheet3(sheet) { var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート9'); // 展開先の行番号(例:150目以降に展開したい場合「150」を指定) var OUTPUT_START = 150; .... ~~略~~(シート3専用の処理) } } function getAddress(sheetObj, row, col) { 略 } function getSprittedRange(rangelist) { 略 }
投稿2021/10/10 03:52
編集2021/10/10 14:14退会済みユーザー
総合スコア0
0
setformulaがA1形式のみ対応らしいのですが、これをiで読み取れるようにするにはどうすればよろしいでしょうか?
下記ではだめなのでしょうか。
js
1 sheet.getRange(i+1, 1).setFormula(`=indirect(address(${rangeList[i].getRow()-1+1},3))`); 2 sheet.getRange(i+1, 2).setValue(`=indirect(address(1,${rangeList[i].getColumn()-1}))`);
投稿2021/10/09 10:12
編集2021/10/09 10:19退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2021/10/09 15:30 編集
2021/10/09 16:15
退会済みユーザー
2021/10/09 23:21 編集
2021/10/09 23:29
0
読解力がなくて申し訳ないですが、次のように理解しました
- アクティブシートで選択したセルがある行の L,M 列の和をシート9の A 列に追記する
- 複数セルを含む範囲が複数個同時に選択され得る
- 範囲に重複があっても複数回転記しない
すみませんが Query でやるやり方はわかりません
javascript
1const q363492 = () => { 2 const samplingSheet = SpreadsheetApp.getActiveSheet(); 3 const insertSheet = SpreadsheetApp.getActive().getSheetByName('シート4'); 4 const insertRowPosition = insertSheet.getDataRange().getValues().map(e=>e[0]).filter(e=>e !== "").length + 1; 5 const selectedRowIndexes = [...new Set(SpreadsheetApp.getActiveSheet().getActiveRangeList().getRanges().map(r=>[...generateRange(r.getRow(),r.getRow() + r.getHeight() - 1)]).flat())].sort().map(e=>e-1); 6 const dat = samplingSheet.getDataRange().getValues().filter((e,i)=>selectedRowIndexes.includes(i)).map(e=>[e[11]+e[12]]); 7 insertSheet.getRange(insertRowPosition,1,dat.length,1).setValues(dat); 8} 9function* generateRange(start, end) { 10 if(start > end) throw Error; 11 for(let i = start; i <= end; i++) { 12 yield i; 13 } 14}
投稿2021/10/08 17:31
総合スコア12705
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/10/09 06:52 編集
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/10/10 08:06
退会済みユーザー
2021/10/10 08:28 編集
2021/10/10 09:56 編集
退会済みユーザー
2021/10/10 10:24
2021/10/10 12:03
退会済みユーザー
2021/10/10 12:20 編集
2021/10/10 13:21
退会済みユーザー
2021/10/10 14:54 編集
2021/10/10 15:13 編集