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

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

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

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

多次元配列

1次元配列内にさらに配列を格納している配列を、多次元配列と呼びます。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

1回答

6778閲覧

【GAS】二次元配列から特定の列のデータをspliceで削除したい

donguriko

総合スコア30

Google Apps Script

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

多次元配列

1次元配列内にさらに配列を格納している配列を、多次元配列と呼びます。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2022/03/27 03:28

編集2022/03/27 03:32

前提

スプレッドシートの中に3つのシートがあります。
①入力シート(mysheet1)
②時間マージシート(mysheet2)
③件数マージシート(mysheet3)

<①入力シート(mysheet1)のイメージ>
イメージ説明

<参考:②時間マージシート(mysheet2)のイメージ>
イメージ説明

<③件数マージシート(mysheet3)のイメージ>
イメージ説明

GASで、①の内容を②と③に転記し、
転記が完了したら、①に「転記済」と追記させたいです。

①→②の転記の際、転記元データはgetValuesで1回で
二次元配列(myCase_Before)として取得させようと
思っています。

実現したいこと

①→②転記の際、「確認欄(G列)」のデータは不要です。

この場合、やり方は2つあると考えました。
a)D列~F列とH列~I列を別々の2つの二次元配列として取得する方法

b)まず、不要なG列を含んだ状態のD列~I列の範囲でまとめて取得。
二次元配列として取得後、G列を配列から削除。

以前の質問で、
「getValuesを使うときは、なるべく広い範囲をまとめて配列として読み込む

配列データ加工(pushやmap等で新しい配列を作る等)

まとめてsetVauesで書き込み」がよい、

とアドバイスをいただきましたので、b)方式でコード記載しました。

発生している問題

別の質問でご教示いただいた内容を見ながらコードを記載しましたが、
うまくG列の削除ができていません。
別質問のリンク

(想定)
配列名.splice(index番号,何個削除するか)

G列は列indexは0はじまりで「3」。
削除したいのは、G列のみなので削除したい個数は「1」としました。

●前回の例が1次元?配列だったので、二次元配列の場合は、
引数として行indexと列index それぞれの指定が必要だから
今回うまくいっていないのでしょうか?

●それとも、spliceでは複数行分をまとめての削除はできず、
1行ずつの一元配列内で削除し、push等で別の配列に再投入ノ
していく必要があるのでしょうか?
この場合、現コードをどのように修正すればよいのかの
アドバイスも欲しいです。

GAS本やネット検索もしてみましたが、自力で答えにたどり着くことが
できませんでした。
次回から自分で、解決できるようになりたいので、
ビギナーでも理解できるレベルでの解説をいただけると助かります。

エラーメッセージ

Exception: The number of rows in the data does not match the number of rows in the range. The data has 1 but the range has 7. Merge20220326 @ マージシートへの転記20220326.gs:75

該当のソースコード

以下、コード全文記載します。

GAS

1function Merge20220326() { 2 const ss = SpreadsheetApp.getActiveSpreadsheet(); 3 const date1 = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyyMMdd'); 4 const date2 = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd'); 5 console.log("date1 " + date1); 6 console.log("date2 " + date2); 7 const mysheet1 = ss.getSheetByName(date1); 8 const mysheet2 = ss.getSheetByName("作業時間マージ"); 9 const mysheet3 = ss.getSheetByName("件数マージ"); 10 11 //▼作業0_「作業時間マージシート(mysheet2)」の処理 12 //「スキャン開始時刻(D)」データあり最終行の行番号(maxrow_mysheet2)を取得 13 //getRange(行番号,列番号,▲行分,■列分) 14 const maxrow_mysheet2 = mysheet2.getRange(mysheet2.getMaxRows(), 4).getNextDataCell(SpreadsheetApp.Direction.UP).getRow() 15 console.log("時間マージ最終行 " + maxrow_mysheet2); 16 17 //▼作業1_「当日分シート(mysheet1)」の処理  18 //「事故番号(E列)」データあり最終行の行番号(maxrow_mysheet1)を取得 19 const maxrow1_mysheet1 = mysheet1.getRange(mysheet1.getMaxRows(), 5).getNextDataCell(SpreadsheetApp.Direction.UP).getRow() 20 21 //「当日分シート(mysheet1)」の「スキャン開始時刻(L列)」データあり最終行の行番号(maxrow2_mysheet1)を取得 22 const maxrow2_mysheet1 = mysheet1.getRange(13, 12).getNextDataCell(SpreadsheetApp.Direction.UP).getRow() 23 console.log("当日シート事故番号最終行maxrow1 " + maxrow1_mysheet1); 24 console.log("当日シートスキャン開始時刻最終行maxrow2 " + maxrow2_mysheet1); 25 const targetRows1 = maxrow1_mysheet1 - 3; 26 const targetRows2 = maxrow2_mysheet1 - 3; 27 console.log("targetRows1 " + targetRows1); 28 console.log("targetRows2 " + targetRows2); 29 30 //▼「時刻」欄の処理 31 //当日分シート(mysheet1)の「スキャン開始時刻(L4)」から「作業時間(N列)」欄の内容を配列(workTime)で取得。 32 //起点はセルK4。配列内の要素は「スキャン開始時刻」「入力完了時刻」「総作業時間」 33 //getRange(行、列、▲行分、■列分) 34 const myRange1 = mysheet1.getRange(4, 12, targetRows2, 3); 35 const workTime = myRange1.getValues(); 36 37 //配列(workTime)で取得した値を「時間マージシート(mysheet2)」に転記 38 //getRange(行、列、▲行分、■列分) 39 mysheet2.getRange(maxrow_mysheet2 +1, 4, targetRows2, 3).setValues(workTime); 40 41 //「時間マージシート(mysheet2)」に日付を補充 42 //getRange(行、列、▲行分、■列分) 43 mysheet2.getRange(maxrow_mysheet2 +1, 3, targetRows2, 1).setValue(date2); 44 45 //当日分シート(mysheet1)への「転記済」フラグ追記 46 //当日分シート(mySheet1)の時刻欄の「転記処理(O列)」欄に「転記済」を追記する 47 //getRange(行、列、▲行分、■列分) 48 mysheet1.getRange(4, 15, targetRows2, 1).setValue("転記済"); 49 50 //▼作業2_「件数マージ」シート(mysheet3)の編集 51 //マージシート「事故番号(D)」データあり最終行の行番号(maxrow_mysheet3)を取得 52 const maxrow_mysheet3 = mysheet3.getRange(mysheet3.getMaxRows(), 4).getNextDataCell(SpreadsheetApp.Direction.UP).getRow() 53 console.log("件数マージ最終行 " + maxrow_mysheet3); 54 55 //当日分シート(mysheet1)の「事故番号(E4)」から「備考(I列)」欄の内容を配列(yCase_Before)で取得。 56 //起点はセルE44。配列内の要素は「事故番号」「担当者」「読」「ステータス」「備考」 57 //getRange(行、列、▲行分、■列分) 58 const myRange2 = mysheet1.getRange(4, 5, targetRows1, 5); 59 const myCase = myRange2.getValues(); 60 console.log("myCase_Before " + myCase); 61 62 //★うまくいかない★ 63 //配列(myCase)で取得した値から、「読」列のデータを削除する。 64 //splice(0はじまりindex,何個削除) 65 const myCase_After = myCase.splice(3,1); 66 console.log("myCase_after " + myCase_After); 67 68 //「件数マージシート(mysheet2)」に日付を補充 69 //getRange(行、列、▲行分、■列分) 70 mysheet3.getRange(maxrow_mysheet3 +1, 3, targetRows1, 1).setValue(date2); 71 72 //配列(myCase)で取得した値を「件数マージシート(mysheet3)」に転記 73 //getRange(行、列、▲行分、■列分) 74 mysheet3.getRange(maxrow_mysheet3 +1, 4, targetRows1, 4).setValues(myCase_After); 75 76 //▼作業3_当日分シートへの「転記済」フラグ追記 77 //当日分シート(mySheet1)の「転記処理(C列)」欄に「転記済」を追記する 78 //getRange(行、列、▲行分、■列分) 79 mysheet1.getRange(4, 3, targetRows1, 1).setValue("転記済"); 80 81 //処理完了メッセージを表示 82 Browser.msgBox("本日分のデータをマージシートに転記しました。\\n 転記処理の列に「転記済」の表示があれば正しく転記できています。\\作業お疲れさまでした。"); 83} 84

試したこと

G列を含んだ状態の配列(myCase_Before)と
G列削除に失敗していると思われる配列(myCase_after)のログデータ
イメージ説明

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

お忙しいところ大変申し訳ありませんが、急ぎませんので
二次元配列でのspliceの使い方につき、解説をいただけないでしょうか?
よろしくお願いいたします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

●前回の例が1次元?配列だったので、二次元配列の場合は、
引数として行indexと列index それぞれの指定が必要だから
今回うまくいっていないのでしょうか?

●それとも、spliceでは複数行分をまとめての削除はできず、
1行ずつの一元配列内で削除し、push等で別の配列に再投入ノ
していく必要があるのでしょうか?

今回の場合は後者が正解に近いです。
getValues()で取得したスプレッドシートのデータ(二次元配列)について、spliceを使って"一発"で「列」を削除することはできません。
(「行」の削除ならば"一発"で可能)

たとえば最初は簡単に、1次元配列で考えてみましょう。

const a = ['あ', 'い', 'う', 'え', 'お']; a.splice(3, 1); console.log(a);

とすると、0 始まりでインデックス3(=4番目)の「'え'」が 1個削除されて
['あ', 'い', 'う', 'お'];
と表示されます。

これを2次元配列の場合にあてはめてみましょう。
getValuesで、5x5の範囲を取得したような場合を仮定し、

const array = [ ['あ', 'い', 'う', 'え', 'お'], ['か', 'き', 'く', 'け', 'こ'], ['さ', 'し', 'す', 'せ', 'そ'], ['た', 'ち', 'つ', 'て', 'と'], ['な', 'に', 'ぬ', 'ね', 'の'] ];

とすると、
array[0] → ['あ', 'い', 'う', 'え', 'お'],
array[1] → ['か', 'き', 'く', 'け', 'こ'],
array[2] → ['さ', 'し', 'す', 'せ', 'そ'],
array[3] → ['た', 'ち', 'つ', 'て', 'と'],
array[4] → ['な', 'に', 'ぬ', 'ね', 'の']
なので、

array.splice(3, 1); console.log(array);

を実行すると、4行目(array[3])の ['た', 'ち', 'つ', 'て', 'と'] が1行削除されて

[ ['あ', 'い', 'う', 'え', 'お'], ['か', 'き', 'く', 'け', 'こ'], ['さ', 'し', 'す', 'せ', 'そ'], ['な', 'に', 'ぬ', 'ね', 'の'] ];

と表示されます。

つまり、行の削除は"一発"でできるわけです。
しかしながら列の削除は、下記のようにしなければなりません。
(ここでは元の2次元配列の各行4列目を削除すると仮定します)

const array = [ ['あ', 'い', 'う', 'え', 'お'], ['か', 'き', 'く', 'け', 'こ'], ['さ', 'し', 'す', 'せ', 'そ'], ['た', 'ち', 'つ', 'て', 'と'], ['な', 'に', 'ぬ', 'ね', 'の'] ]; for (let i = 0; i < array.length; i++) { // 1行ずつ取り出して、インデックス3(4番目)を1個削除する。 array[i].splice(3,1); } console.log(array);

実行結果

[ [ 'あ', 'い', 'う', 'お' ], [ 'か', 'き', 'く', 'こ' ], [ 'さ', 'し', 'す', 'そ' ], [ 'た', 'ち', 'つ', 'と' ], [ 'な', 'に', 'ぬ', 'の' ] ]

array[i] = 各行であり、各行の1次元配列に対してsplice(3, 1)を行っています。
このように(一手間かかりますが)ループで回すことにより全行の列の削除を行うことができます。


【補足】
なお、spliceは、元の配列そのものを変更します
(元の配列そのものを変更する関数を「破壊的メソッド」と言ったりします)
したがって、今回の場面では、戻り値を利用する必要はありません。
splice関数の戻り値は、切り取られた後の配列ではなく、切り取った部分になります。
例:

const a = ['あ', 'い', 'う', 'え', 'お']; const b = a.splice(3, 1); // spliceは、元の配列そのものを変更する。 console.log(a); >>> ['あ', 'い', 'う', 'お']; // spliceの戻り値は、切り取った部分である。 console.log(b); >>> ['え']

上記を踏まえた修正点は下記です。(先頭が+となっている行は追加、-となっている行は削除)
(63行目あたりから)

diff

1 //★うまくいかない★ 2 //配列(myCase)で取得した値から、 3 //splice(0はじまりindex,何個削除) 4- const myCase_After = myCase.splice(3,1); 5+ for (let i=0; i < myCase.length; i++) { 6+ myCase[i].splice(2, 1); // 「読」列のデータは3列目なので、「2」を指定 7+ } 8 9- console.log("myCase_after " + myCase_After); 10+ console.log("myCase_after " + myCase); 11 12 //「件数マージシート(mysheet2)」に日付を補充 13 //getRange(行、列、▲行分、■列分) 14 mysheet3.getRange(maxrow_mysheet3 +1, 3, targetRows1, 1).setValue(date2); 15 16 //配列(myCase)で取得した値を「件数マージシート(mysheet3)」に転記 17 //getRange(行、列、▲行分、■列分) 18- mysheet3.getRange(maxrow_mysheet3 +1, 4, targetRows1, 4).setValues(myCase_After); 19+ mysheet3.getRange(maxrow_mysheet3 +1, 4, targetRows1, 4).setValues(myCase);

修正後の全体コードは下記になります。

js

1function Merge20220326() { 2 const ss = SpreadsheetApp.getActiveSpreadsheet(); 3 const date1 = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyyMMdd'); 4 const date2 = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd'); 5 console.log("date1 " + date1); 6 console.log("date2 " + date2); 7 const mysheet1 = ss.getSheetByName(date1); 8 9 const mysheet2 = ss.getSheetByName("作業時間マージ"); 10 const mysheet3 = ss.getSheetByName("件数マージ"); 11 12 //▼作業0_「作業時間マージシート(mysheet2)」の処理 13 //「スキャン開始時刻(D)」データあり最終行の行番号(maxrow_mysheet2)を取得 14 //getRange(行番号,列番号,▲行分,■列分) 15 const maxrow_mysheet2 = mysheet2.getRange(mysheet2.getMaxRows(), 4).getNextDataCell(SpreadsheetApp.Direction.UP).getRow() 16 console.log("時間マージ最終行 " + maxrow_mysheet2); 17 18 //▼作業1_「当日分シート(mysheet1)」の処理  19 //「事故番号(E列)」データあり最終行の行番号(maxrow_mysheet1)を取得 20 const maxrow1_mysheet1 = mysheet1.getRange(mysheet1.getMaxRows(), 5).getNextDataCell(SpreadsheetApp.Direction.UP).getRow() 21 22 //「当日分シート(mysheet1)」の「スキャン開始時刻(L列)」データあり最終行の行番号(maxrow2_mysheet1)を取得 23 const maxrow2_mysheet1 = mysheet1.getRange(13, 12).getNextDataCell(SpreadsheetApp.Direction.UP).getRow() 24 console.log("当日シート事故番号最終行maxrow1 " + maxrow1_mysheet1); 25 console.log("当日シートスキャン開始時刻最終行maxrow2 " + maxrow2_mysheet1); 26 const targetRows1 = maxrow1_mysheet1 - 3; 27 const targetRows2 = maxrow2_mysheet1 - 3; 28 console.log("targetRows1 " + targetRows1); 29 console.log("targetRows2 " + targetRows2); 30 31 //▼「時刻」欄の処理 32 //当日分シート(mysheet1)の「スキャン開始時刻(L4)」から「作業時間(N列)」欄の内容を配列(workTime)で取得。 33 //起点はセルK4。配列内の要素は「スキャン開始時刻」「入力完了時刻」「総作業時間」 34 //getRange(行、列、▲行分、■列分) 35 const myRange1 = mysheet1.getRange(4, 12, targetRows2, 3); 36 const workTime = myRange1.getValues(); 37 38 //配列(workTime)で取得した値を「時間マージシート(mysheet2)」に転記 39 //getRange(行、列、▲行分、■列分) 40 mysheet2.getRange(maxrow_mysheet2 + 1, 4, targetRows2, 3).setValues(workTime); 41 42 //「時間マージシート(mysheet2)」に日付を補充 43 //getRange(行、列、▲行分、■列分) 44 mysheet2.getRange(maxrow_mysheet2 + 1, 3, targetRows2, 1).setValue(date2); 45 46 //当日分シート(mysheet1)への「転記済」フラグ追記 47 //当日分シート(mySheet1)の時刻欄の「転記処理(O列)」欄に「転記済」を追記する 48 //getRange(行、列、▲行分、■列分) 49 mysheet1.getRange(4, 15, targetRows2, 1).setValue("転記済"); 50 51 //▼作業2_「件数マージ」シート(mysheet3)の編集 52 //マージシート「事故番号(D)」データあり最終行の行番号(maxrow_mysheet3)を取得 53 const maxrow_mysheet3 = mysheet3.getRange(mysheet3.getMaxRows(), 4).getNextDataCell(SpreadsheetApp.Direction.UP).getRow() 54 console.log("件数マージ最終行 " + maxrow_mysheet3); 55 56 //当日分シート(mysheet1)の「事故番号(E4)」から「備考(I列)」欄の内容を配列(yCase_Before)で取得。 57 //起点はセルE44。配列内の要素は「事故番号」「担当者」「読」「ステータス」「備考」 58 //getRange(行、列、▲行分、■列分) 59 const myRange2 = mysheet1.getRange(4, 5, targetRows1, 5); 60 const myCase = myRange2.getValues(); 61 console.log("myCase_Before " + myCase); 62 63 //★うまくいかない★ → 修正 64 //配列(myCase)で取得した値から、「読」列のデータを削除する。 65 //splice(0はじまりindex,何個削除) 66 for (let i=0; i < myCase.length; i++) { 67 myCase[i].splice(2, 1); // 「読」列のデータは3列目なので、「2」を指定 68 } 69 console.log("myCase_after " + myCase); 70 71 //「件数マージシート(mysheet2)」に日付を補充 72 //getRange(行、列、▲行分、■列分) 73 mysheet3.getRange(maxrow_mysheet3 + 1, 3, targetRows1, 1).setValue(date2); 74 75 //配列(myCase)で取得した値を「件数マージシート(mysheet3)」に転記 76 //getRange(行、列、▲行分、■列分) 77 mysheet3.getRange(maxrow_mysheet3 + 1, 4, targetRows1, 4).setValues(myCase); // 修正 78 79 //▼作業3_当日分シートへの「転記済」フラグ追記 80 //当日分シート(mySheet1)の「転記処理(C列)」欄に「転記済」を追記する 81 //getRange(行、列、▲行分、■列分) 82 mysheet1.getRange(4, 3, targetRows1, 1).setValue("転記済"); 83 84 //処理完了メッセージを表示 85 Browser.msgBox("本日分のデータをマージシートに転記しました。\\n 転記処理の列に「転記済」の表示があれば正しく転記できています。\\作業お疲れさまでした。"); 86}

投稿2022/03/27 09:16

編集2022/03/27 09:27
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

donguriko

2022/03/27 09:56

qnoirさま お忙しい中、早々の回答、解説ありがとうございます!! そして、解説とても分かりやすいです。 getValues()で取得した二次元配列でのspliceの使い方、 イメージがつきました。 列を削除したい場合はforループで1行ずつ削除しないといけないのですね。 そして、戻り値は「切り取った部分」なのですね。 目からウロコです。勘違いしていました。 勉強になります!! 今回の回答も、「永久保存版」として大切に保管し、自分のモノになるまで 何度も読み返したいと思います。 いつも丁寧な解説、本当にありがとうございます。 qnoirさまのおかげで、配列について以前に比べ、イメージが つかめてきました。 <さら問い> 参考まで、教えてください。 getValues()で一発で配列を作成する際、「確認欄(G列)」を除外するために 今回私はb)方式を選択しましたが、もしqnoirさまでしたらa)とb)どちらを 選びますか? そして、なぜそちらなのかも教えていただけますか? 今後類似のケースがあった場合の「考え方」の参考にさせていただきたく。
退会済みユーザー

退会済みユーザー

2022/03/27 10:22

今回の場合なら b) を選びます。 理由としては、a)に比べて、getValues/setValuesの呼び出し回数を少なくできるからです。
donguriko

2022/03/28 12:43

qnoirさま 今回のケースだとb)で正解とのこと。 方向性外してなくてよかったです。 お忙しい中、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問