こんにちは!
Yuki (@yukibnb) です。
以前Google Apps Script (以降GAS) でスプレッドシートのセル範囲や値を取得する方法を紹介しました。
www.yukibnb.com
その中でスプレッドシートのgetValuesで取得した値は二次元配列で変数に格納されるとお話ししました。
GASで二次元配列を理解することはとても重要なのですが、プログラミングを勉強し始めたばかりの方にとっては中々理解するのが難しいと思います。実際に僕も四苦八苦しました(^^;
何度も試行錯誤しながらチャレンジしていくうちに徐々に使いこなせるようになり、今では苦手意識なく二次元配列を使用することができています。
この記事では昔の僕のようにGASを使い始めたが二次元配列の理解に苦しんでいる方向けに、図入りでかなりかみ砕いてやさしく紹介したいと思います。
ある程度経験のある方にとっては少しくどく感じるかもしれませんので、適宜読み飛ばしてください。
では二次元配列を詳しく見ていきましょう!
この記事の対象者や目標
対象者
この記事では次のような方を対象にしています。
- GASやプログラミングを始めたばかり
- 二次元配列以前にそもそも配列が何かよくわからない
- ループ(for文など)を使ってスプレッドシートを操作するスクリプトを書いたが何故か処理速度が遅い
- 二次元配列を勉強しているがつまづいてしまった
- ループ(for文)やif文はなんとなく使える
目標
この記事を読むと次のことができるようになります!(なるはず!)
- 二次元配列の仕組みや見方が理解できる
- 一次元配列と二次元配列の違いがわかる
- GASではなぜ二次元配列を理解することが重要かわかる
そして次のステップへ
長さの関係でこの記事では紹介できませんが、二次元配列を理解して使いこなせるようになると次のようなことが高速でできるようになります。
- 日直を毎日担当者にメールやLINEで自動通知
- スプレッドシートから条件を満たす値を抽出、修正、削除する
- スプレッドシートから条件を満たす値のテキスト色や背景色を編集する
- スプレッドシートから条件を満たす値の個数を集計する
- スプレッドシートから条件を満たす数値の合計数を計算する
これらの具体的なサンプルコードはまた別記事にて紹介、解説したいと思います。
まずこの記事では二次元配列の仕組みを理解することに集中しましょう!
そもそもの前提知識
なぜ二次元配列が重要か
「二次元配列はこうやって使います!」という使用方法を紹介する前に、そもそもGASで二次元配列はどうして重要なのか気になりますよね。
以下2点が大きなカギです。
- スクリプト内でAPIを使用するとその都度処理に時間がかかる
- スクリプト1回の実行につき6分を超えると停止する
※Google Workspace (旧G Suite) のBusiness/Enterprise/Educationプランを契約している方は1回の実行につき30分までスクリプトが動きます。以下GASの公式リファレンス参照 (英語)。
https://developers.google.com/apps-script/guides/services/quotas
APIとはGASでいうところの以下のようなものです。
- getRange()
- getValue()
- getValues()
- setValue()
- setValues()
- getLastRow()
- getLastColumn()
これらを使用すると都度Googleにリクエスト(お願い)をすることになります。
例えば『getValue("A1")』を実行すると「Googleさん、A1セルの値を教えて」というお願いをします。Googleは気前よく「5ですよ」と教えてくれます。
では続けて『getValue("A2")』のように「Googleさん、じゃあ次はA2セルの値を教えて」とお願いをします。Googleはまだ気前よく「3ですよ」と教えてくれます。
ではではさらに続けて『getValue("A3")』『getValue("A4")』『getValue("A5")』.....『getValue("A100")』と聞くとどうでしょう?
人間と違ってGoogleは怒らずに値を教えてくれますがどうしても時間が掛かってしまいます。
僕だったら「Yukiさん、A1セルからA100セルまでの値を教えて」と1回でお願いされた方がはるかにうれしいですし、何よりスピードが速く対応できます。
きっとGoogleも同じことを思っています。
極力Googleにリクエスト(お願い)する回数を減らすとそれだけスピードが速くなり、複雑な処理でも6分以内に完了させることが可能になります。
ではどのようにGoogleにリクエストする回数を減らしつつ、求める結果を出すのでしょうか?
それを実現するのが二次元配列です!
二次元配列を活用するとAPIの使用回数(Googleへのリクエスト)を大幅に減らすことが可能になり、高速で処理を完了できます。
限られた時間の中で複雑な処理を完了するためには二次元配列の理解は避けて通れません。具体的な二次元配列の活用方法は本記事後半で紹介しますので、まずはGASで高速化を実現するためには二次元配列の理解は重要なんだと覚えてください ^^
一次元配列とは
まず二次元配列よりも簡単な一次元配列とは何かを見てみましょう。
一次元配列とは以下のようなものです。
var data = ["東京都","神奈川県","大阪府","京都府","広島県"];
dataという箱の中に複数の値を保管しているイメージです。なおdataという名前は僕が勝手に名付けたものなので皆さん自身でどのように名付けても大丈夫です。
箱の中の値は取り出して使用したり、編集したり、追加したり、削除したりすることが可能です。
例えば「私の出身地は大阪府です。」とログに残したい場合は以下のように書きます。
var data = ["東京都","神奈川県","大阪府","京都府","広島県"]; //ログに「私の出身地は大阪府です。」と表示されます。 Logger.log("私の出身地は" + data[2] + "です。");
data[2]と書くと配列名dataの中のインデックス番号2番目の値、つまり「大阪府」を取り出すことができます。
- 配列名[インデックス番号] 例: data[2]
『あれ、「大阪府」は配列の中で3番目だからdata[3]じゃないの?』と思った方もいると思います。
配列ではインデックス番号は0からスタートします。そのため今回の例では以下のようになります。
var data = ["東京都","神奈川県","大阪府","京都府","広島県"]; data[0] //東京都 data[1] //神奈川県 data[2] //大阪府 data[3] //京都府 data[4] //広島県
このように一次元配列は比較的イメージしやすいかと思います。
二次元配列とは
ではいよいよ二次元配列を見てみましょう。
二次元配列とは以下のようなものです。
var data = [["東京都","スカイツリー","東京タワー"],["神奈川県","中華街","赤れんが倉庫"],["大阪府","スカイビル","大阪城"],["京都府","清水寺","八坂神社"],["広島県","厳島神社","尾道"]];
二次元配列も一次元配列と同じように箱の中に様々な値を保管しているイメージです。ただし一次元配列と違う点は[ ]の中にさらに[ ]が入っていることです。
つまり箱の中にさらに複数の箱があり、その複数の箱の中に値が入っています。
一次元配列の例では都道府県だけでしたが、今回は各都道府県内の名所も入力されています。
なお上記の二次元配列はかなり横長ですが、以下のように改行することも可能です。
var data = [["東京都","スカイツリー","東京タワー"], ["神奈川県","中華街","赤れんが倉庫"], ["大阪府","スカイビル","大阪城"], ["京都府","清水寺","八坂神社"], ["広島県","厳島神社","尾道"]];
一次元配列と同様に箱の中の値は取り出して使用したり、編集したり、追加したり、削除したりすることが可能です。
例えば「私は京都府の清水寺に行きたいです。」とログに残したい場合は以下のように書きます。
var data = [["東京都","スカイツリー","東京タワー"], ["神奈川県","中華街","赤れんが倉庫"], ["大阪府","スカイビル","大阪城"], ["京都府","清水寺","八坂神社"], ["広島県","厳島神社","尾道"]]; //ログに「私は京都府の清水寺に行きたいです。」と表示されます。 Logger.log("私は" + data[3][0] + "の" + data[3][1] + "に行きたいです。");
一次元配列の値の取り出し方と異なる点はdata[3][0]のように[ ]を二つ使用することです。
使用イメージは以下です。
このように二次元配列から値を取り出す方法は、①まず大きな箱である配列名を指定し、②配列の中の小分けされた箱(一つ目の[ ])を選び、③その箱の中に入っているもの(二つ目の[ ])を取り出すというイメージです。
一次元配列から順を追って細かく見てみると理解しやすいですね。
前置きが長くなりましたが、いよいよGAS(特にスプレッドシート)で二次元配列をどのように活用できるのか実践しましょう!
実践!二次元配列の使い方
二次元配列とスプレッドシートの関係
この記事ではここまで二次元配列そのものの説明をしてきましたが、二次元配列とスプレッドシートの関係性についてはまだお話ししていませんでした。
例えば以下のようなスプレッドシートのシートがあるとします。
A1からC6の表の値を1回で取得したい場合、このようなコードになります。
//アクティブなスプレッドシートを取得 var ss = SpreadsheetApp.getActiveSpreadsheet(); //アクティブなシートを取得 var sh = ss.getActiveSheet(); //A1~C6の値をgetValuesで取得 var table = sh.getRange(1,1,6,3).getValues(); //ログでtableを確認 Logger.log(table);
A1からC6の値は「var table」に格納されました。
「var table」にはどのような状態で値が格納されていると思いますか?ログで確認しましょう。
みなさん想像がついていると思います。
「var table」には二次元配列で値が格納されています。
つまりgetValues()を使うと二次元配列で値が格納されるわけです。
上記ログのスクリーンショットを見ると急に複雑になった気がしますが、改行して見た目を整えてあげると以下のようになります。
[[名前, 支店名, 部署], [山田, 東京, 営業], [鈴木, 大阪, 営業], [伊藤, 大阪, 経理], [田中, 福岡, 人事], [林, 北海道, 総務]]
こうやって見るとスプレッドシートの表と同じような見た目ですね!
この記事の冒頭でgetValue()を何回も使用してGoogleにお願いすると時間が掛かるとお伝えしました。A1からC6までの一つ一つのセルの値をgetValue()すると18回もgetValue()しないといけません。
getValues()を使えば1回だけでA1からC6までの値を取得できます。1回だけのお願いなので高速です。
以上のことからGASでスプレッドシートの値を取得する場合、Googleにお願いする回数が少なく且つ高速に処理できるgetValues()を使うのがおすすめです。
そして「getValues()を使う」イコール「二次元配列を扱う」ことになるため、スプレッドシートと二次元配列は切っても切れない関係なのです。
値の取り出し方
二次元配列「var table」に格納された値を取り出す方法はすでに学習した方法と同じです。
例えば「田中さんは福岡支店の人事部で働いています。」とログに残したい場合は以下のように書きます。
//アクティブなスプレッドシートを取得 var ss = SpreadsheetApp.getActiveSpreadsheet(); //アクティブなシートを取得 var sh = ss.getActiveSheet(); //A1~C6の値をgetValuesで取得 var table = sh.getRange(1,1,6,3).getValues(); /* table内は以下のように二次元配列になっています [[名前, 支店名, 部署], [山田, 東京, 営業], [鈴木, 大阪, 営業], [伊藤, 大阪, 経理], [田中, 福岡, 人事], [林, 北海道, 総務]] */ //ログに「田中さんは福岡支店の人事部で働いています。」と表示されます。 Logger.log(table[4][0] + "さんは" + table[4][1] + "支店の" + table[4][2] + "部で働いています。");
つまり、二次元配列のひとつめの[ ]は行を、ふたつめの[ ]は列を表します。
これまでの説明で二次元配列の中の値の取り出し方は①まず大きな箱である配列名を指定し、②配列の中の小分けされた箱(一つ目の[ ])を選び、③その箱の中に入っているもの(二つ目の[ ])を取り出すというイメージとお伝えしました。
これをスプレッドシートの用語に置き換えると以下のようなイメージになります。
大事な3つのポイント
getValues()を使ってスプレッドシートの表にある値を1回でまとめて二次元配列で取得できることはわかりました。
取得した後は主に以下のような流れで使用します。
- 値を二次元配列で取得する ※表の値をまとめて1回で取得するので高速
- 二次元配列を編集する ※APIを使用しないので高速編集が可能
- 編集した二次元配列を使用する (主な例は以下)
- 編集した値をスプレッドシートに貼り付ける
- 特定の値を抽出してメールやLINEで通知する
二次元配列とはあくまでスプレッドシートの値を取得して箱に入れているだけです。二次元配列の中の値を編集するだけではスプレッドシートに反映されません。
※「二次元配列の中の値を編集する」=「APIを使用しないですむ」=「高速で処理できる」というイメージです。
もしスプレッドシートの値を変更したい場合、二次元配列を編集した後にスプレッドシートに貼り付ける必要があります。
「なんだかめんどくさそう」と思うかもしれませんが、使っていくうち取得・編集・使用(貼り付けなど)の3ステップは自然と身につきます。
簡単な実例紹介
では最後にいくつか簡単な実例を紹介したいと思います。
あくまで簡易的なものなので、より具体的な実例は別記事にて紹介したいと思います。
データは先ほど使った以下スプレッドシートを使用します。なおスクリプト内ではループ(for文)やif文も使用しています。
ループ(for文)やif文はまだちょっとわからないという場合はさらっと読み飛ばしてください。
【全員の名前に「さん」を付ける】スクリプト
//アクティブなスプレッドシートを取得 var ss = SpreadsheetApp.getActiveSpreadsheet(); //アクティブなシートを取得 var sh = ss.getActiveSheet(); //A1~C6の値をgetValuesで取得 //※※tableには二次元配列で値が格納されます※※ var table = sh.getRange(1,1,6,3).getValues(); //1行目(名前、支店名、部署)は変更しないので、iは1からスタート for(var i = 1; i < table.length; i++) { //i行目のインデックス0、つまり名前にさんをつける table[i][0] = table[i][0] + "さん"; } //↑この時点では二次元配列tableの値に「さん」を付けただけ // なので、スプレッドシートにtableの値を貼り付けます。 //A1~C6に編集したtableの値を貼り付けます。 //setValuesは二次元配列を貼り付けるメソッドです。 sh.getRange(1,1,6,3).setValues(table); //↑setValuesでtableの値をスプレッドシートに貼り付ける // ことで、スプレッドシートに「さん」が反映されました。
【部署名を「営業」から「セールス」に変更する】スクリプト
//アクティブなスプレッドシートを取得 var ss = SpreadsheetApp.getActiveSpreadsheet(); //アクティブなシートを取得 var sh = ss.getActiveSheet(); //A1~C6の値をgetValuesで取得 //※※tableには二次元配列で値が格納されます※※ var table = sh.getRange(1,1,6,3).getValues(); //1行目(名前、支店名、部署)は変更しないので、iは1からスタート for(var i = 1; i < table.length; i++) { //i行目のインデックス2、つまり部署名が「営業」の場合 //「セールス」に値を変更する if(table[i][2] == "営業") { table[i][2] = "セールス" } } //↑この時点では二次元配列table内の「営業」が「セールス」 // に変更されただけなので、スプレッドシートにtableの値を // 貼り付けます。 //A1~C6に編集したtableの値を貼り付けます。 //setValuesは二次元配列を貼り付けるメソッドです。 sh.getRange(1,1,6,3).setValues(table); //↑setValuesでtableの値をスプレッドシートに貼り付ける // ことで、スプレッドシートに「セールス」が反映されました。
【大阪支店で働く人の名前を別の配列に格納する】スクリプト
//アクティブなスプレッドシートを取得 var ss = SpreadsheetApp.getActiveSpreadsheet(); //アクティブなシートを取得 var sh = ss.getActiveSheet(); //A1~C6の値をgetValuesで取得 //※※tableには二次元配列で値が格納されます※※ var table = sh.getRange(1,1,6,3).getValues(); //大阪支店で働く人の名前を格納するための空の配列を準備 var osakaStaff = []; //1行目(名前、支店名、部署)は変更しないので、iは1からスタート for(var i = 1; i < table.length; i++) { //i行目のインデックス1、つまり支店名が「大阪」の場合 //名前(i行目のインデックス0)を配列osakaStaffに格納する if(table[i][1] == "大阪") { //pushとは配列osakaStaffに値を追加するメソッドです。 osakaStaff.push(table[i][0]); } } //大阪支店で働く「鈴木」「伊藤」さんの2名の名前が一次元配列で格納されています Logger.log(osakaStaff);
まとめ
今回は二次元配列の超初心者向けに二次元配列とはどんなものかを細かくわけて紹介しました。
最後にいくつか実例を紹介しましたが、この記事では二次元配列をどうやって使うかよりもそもそもどのようなものかを理解できるような内容を心がけました。
自分がつまづいたポイントを中心に紹介しましたが、こういう覚え方・考え方の方がわかりやすいというアドバイスがありましたら是非コメント頂ければうれしいです!
また別の記事でより具体的な二次元配列の操作方法などをサンプルスクリプトを交えて紹介したいと思います!
【2020/1/28更新】
二次元配列をfor文でループする時の処理順序をGIF動画を用いて視覚的にわかりやすく解説する記事を公開しました。
for文をネスト(入れ子)する時は特にこんがらがりやすいので、記事が参考になればうれしいです!