見出し画像

【GASの書き方覚書②】2次元配列の処理は「ちぎっては投げ」+アレが最強

Google Apps Script (GAS)を、もっと身近に、日々の暮らしに。

シリーズ「GASの書き方覚書」の第二弾は、2次元配列の処理についてです。

GASを始めて早一年。ようやく最近になって、2次元配列を処理するならこれが最強と思えるお作法がわかってきましたので、例によって、図を使いながら説明していきたいと思います。


基本の2次元配列処理(for文)

まずは、基本の2次元配列の処理について確認しましょう。

はじめに2次元配列ですが、以下の図が示す通り、ほぼほぼスプレッドシートの表をイメージしてもらえればOKです。

そりゃGASでガンガンに使いますよね

2次元配列は、行のインデックス番号と列のインデックス番号を持ちます。【GAS活用術①-7】でも説明した通り、インデックス番号は1からではなく、0から始まります。

さて、上記の配列:regsは、ある一日にフォームから登録されたデータを表しています。この配列のデータのうち、「読み聞かせ日」から「本の名前1」まで(列のインデックス番号1から6まで)を、以下のように”:”(コロン)で区切って表示したいとします。

2次元配列のデータを読み、最初の行から最後の行まで必要なデータを取り出して、”:”で区切ったメッセージを生成する必要があります。

基本のfor文を使う場合では、下記のように一行ずつ処理するために、行のインデックス番号を変数 i として、0から1つずつ増やしていき、配列の行数だけ繰り返しています。

変数のmsgLinesに、2次元配列のデータを格納する部分を、基本のfor文を使って記述した場合は、以下のようなコードになります。

  //配列regsの行数だけ繰り返し
  for(let i = 0; i < regs.length ; i++) {
    msgLines += "\n" + regs[i][1] + ": " 
              + regs[i][2] + ": "
              + regs[i][3] + ": "
              + regs[i][4] + ": "
              + regs[i][5] + ": "
              + regs[i][6] ; 
  }

基本に忠実に、iを0から始めて1つずつ増やしていってますよね。

また、2次元配列の値の取得方法は以下の通りです。

2次元配列の値(要素)を取得する時は
  配列名[行のインデックス番号][列のインデックス番号]

そのため、regs[i][数字] が繰り返し記述されています。

for文は、手動でゴリゴリとインデックス番号を1つずつ増して処理していて、行と列のインデックス番号を強く意識する必要がある
、ということを改めて確認しておきましょう。

for-of 文を使った場合

これに対して、for-of文は、配列に含まれる要素(2次元配列なら行)を順に取り出し、処理をしてくれます

イメージ的には、チョコレートなら一行ずつバキッと切り分ける、マンションなら一階ずつズバッと切り取る、そんな感じです(どんな?)。

イラストAC より「さゆりむ」さんの「板チョコ」

for-of 文は以下のように記述します。

口語調で説明するなら、「for-of 文で1行ずつ取り出して処理して!取り出した1行はregって名前にするね、あ、元の2次元配列はregsだからね」という感じでしょうか。

for-of文でofが使われていますが、下記のサイトなどにあるように、英語のofは「取り出す」感覚なのでまさしくですね。

ここでは2次元配列から取り出した1行の変数名を、regとしています。2次元配列を複数形(ここではregs)にして、取り出す1行を表す変数名を単数形(reg)にすることも多いようで、それに倣いましたが、何でも構いません。私はこの場合の変数名として、行を表す「row」を使うことも多いです。

で、さきほどの例のコードをfor-of 文を使って書き換えてみると、

  //配列regsの行数だけ繰り返し
  for (const reg of regs){
    msgLines += "\n" + reg[1] + ": " 
              + reg[2] + ": "
              + reg[3] + ": "
              + reg[4] + ": "
              + reg[5] + ": "
              + reg[6] ; 
  };

おわかりでしょうか。行のインデックス番号の i が消えましたね!

2次元配列に対して使うと、for-of 文は1行(1次元配列)ずつ、順に取り出してくれるので、2次元配列の行のインデックス番号を意識しないでもいいのです。

細かいことは何も言わずとも、最初の行から最後の行まで、1行ずつちぎっては投げ(1次元配列ずつ取り出しては処理)してくれる、強力な2次元配列処理がfor-of文なのです。

さらに分割代入を使えば「鬼に金棒」

さらにアレ、そう、【GASの書き方覚書①】で紹介した分割代入を使えば、こんな風に記述することもできます。

  for (const reg of regs){
    const [, date, type, nen, kumi, person, book1] = reg;
    msgLines += "\n" + date + ": " 
              + type + ": "
              + nen + ": "
              + kumi + ": "
              + person + ": "
              + book1 ; 
  }

列のインデックス番号もなくなりましたね!
さらにさらに、regを省略して、以下のような記述も可能です。

  for (const [, date, type, nen, kumi, person, book1] of regs){
    msgLines += "\n" + date + ": " 
              + type + ": "
              + nen + ": "
              + kumi + ": "
              + person + ": "
              + book1 ; 
  }

ちょっと頭でっかちなので、好みは分かれるかもしれませんが。。。

分割代入を図にすると以下のようなイメージです。

for-of 文で1次元配列ずつ取り出した途端にすぐに分割代入したり、1次元配列を分割代入を使って取り出したりするイメージですね。

また、分割代入では、配列の不要な要素(上記の例では、0、7、8番目の要素)は、余分なカンマをつけて切り捨てる、または、変数の数を減らすことで残りの要素は切り捨てる、といったことが可能です。

for-of 文と分割代入は、まさに鬼に金棒、最強コンビと言えそうです。

本当の推しはforEachメソッドだけど。。

for-of 文と同じように、「ちぎっては投げ」してくれる、forEach メソッドというのもあります。forEachは、以下のように記述します。

この書き方に慣れていないうちは

  • 突然、reg?どこから来た?

  • => ?? アロー関数って???

と「?」が飛びまくりますよね。すごく簡単にいうと、

  • ここでのregは、無名関数の引数となる。本来は、function(reg)と書くところを、=> (アロー関数)を使うことで、functionが省略可能で、さらに引数が一つのため、( )も省略している。

  • 結果、=> (アロー関数)とforEachと組み合わせて2次元配列に対して使用する場合は、1次元配列を表す変数名は、宣言不要となる。

となりますが、要するに、「regsって2次元配列を1行ずつ取り出して!その1行はregって名前にして、この後の処理をやってね〜」ってことです。

forEach、アロー関数、さらに分割代入を使って書き換えたコードは、以下の通りです。

  regs.forEach(reg => {
    const [, date, type, nen, kumi, person, book1] = reg; 
    msgLines += "\n" + date + ": " 
              + type + ": "
              + nen + ": "
              + kumi + ": "
              + person + ": "
              + book1 ; 
  });

regを省略したバージョンも。

  regs.forEach(([, date, type, nen, kumi, person, book1]) => {
    msgLines += "\n" + date + ": " 
              + type + ": "
              + nen + ": "
              + kumi + ": "
              + person + ": "
              + book1 ; 
  });

この時、1次元配列の[ ]の記号を( )で囲う必要があるようです。ご注意ください。

実は、私の本当の推しは、for-of 文より、アロー関数を使ったforEachなんです。とっつきにくいけど、慣れてしまえば、こちらの方が感覚的にわかりやすいと思うんですが。。。

ところが、forEachメソッドは現在、非推奨とのこと。。理由としては、「反復の途中で処理を中断することができない」などが挙げられるようです(詳細は、「forEach 非推奨」でググってみてください)。

個人的には、仕事(商用)で使っているわけではないので、非推奨と言われても、forEachメソッドを使っても問題ないと思っています(私の記事のコードでforEachが使われているのは、推し活ということでご了承ください)。

おわりに

以上が、私の思う、2次元配列を処理する最強の方法です。

for-of 文(またはforEach メソッド)で、「ちぎっては投げ、ちぎっては投げ」で、さらに分割代入を使えば、行や列のインデックス番号を意識せずに2次元配列が処理できます。

GASではスプレッドシートの値をガツっと2次元配列で読み込んでから、あれこれ処理することが多いのですが、ここまで理解するのに結構時間がかかりました。同じように苦労されている方の参考になれば幸いです。

なお、今回の使用したコードは、【GAS活用術⑤-2】のコードを一部抜粋しました。全体のコードは、以下の記事に載せています。

また、分割代入については以下の【GASの書き方覚書①】で説明しています。

この記事が気に入ったらサポートをしてみませんか?