みなさん、こんにちは!
タカハシ(@ntakahashi0505)です。
Google Apps Scriptでスプレッドシートのデータを配列に格納して操作するテクニックについてシリーズでお伝えしています。
前回の記事はコチラ。
配列やオブジェクトなどの便利機能を搭載するライブラリUnderscore for GASの紹介と導入の仕方についてお伝えしました。
さて、なんでわざわざUnderscore for GASを導入したかというと、今回使うzipメソッドが使いたい!というのが最大の理由です。
スプレッドシートのデータは二次元配列として操作することができますが、zip.applyメソッドを使うとその行と列の入れ替えが簡単にできます。
行列転置?…ややこしそうだな~…なんて思わずに、騙されたと思ってみてみて下さい。それはもう配列操作の世界が劇的に広がります。
ということで、今回はGoogle Apps ScriptでUnderscoreのzip.applyメソッドで二次元配列の行と列を入れ替える方法とその意義についてお伝えします。
では、行ってみましょう!
GASでVLOOKUP関数的な検索をしたい
GASでスプレッドシートの「VLOOKUP関数的な検索」をしたいことって結構あります。
つまり、指定した列の中から値を検索して、マッチしたレコードの行番号(または別の列の値)を引っ張ってくるという操作です。
例えば、以下のようなシート「成績表」があったとします。
このシートについて、B列の名前が「Lucas」ってやつの行番号って何番だっけ?
それなら、5行目だよ。
という処理をしたいということです。
スプレッドシートAPIを使って検索する
これをGASでやろうとすると、以下のようなスクリプトを作成することになります。
//sheet:対象シート //val:検索値 //col:検索対象とする列番号 function findRow(sheet,val,col){ var lastRow=sheet.getDataRange().getLastRow(); //対象となるシートの最終行を取得 for(var i=1;i<=lastRow;i++){ if(sheet.getRange(i,col).getValue() === val){ return i; } } return 0; }
Sheetオブジェクト、検索値、検索対象の列番号を渡すと、最初にマッチした行の行番号を返すというものです。
for文の中にスプレッドシートのAPIが含まれてしまっているので、処理速度的に痛々しいですね。
シートのデータを二次元配列に格納して検索する
速度を上げるために、配列でやることもできますね。以下のようなスクリプトになります。
//sheet:対象シート //val:検索値 //col:検索対象とする列番号 function findRow(sheet,val,col){ var dat = sheet.getDataRange().getValues(); //受け取ったシートのデータを二次元配列に取得 for(var i=1;i<dat.length;i++){ if(dat[i][col-1] === val){ return i+1; } } return 0; }
APIを使わなくなるので、処理速度は速くなります。
ここまでの話、以下の記事で詳しく紹介していますので、ご興味があればご覧ください。
配列で検索ならindexOf関数を使えばよい?
ですが、こんな処理、いっぱい使いますもんね。わざわざ関数を作らずとも、もっと簡潔にやりたいじゃないですか。
そこで、パッと思いつくのが、前々回の記事でお伝えしたindexOfメソッドです。
indexOfメソッドでは、以下のようにすることで、配列の先頭の要素から値を検索し、見つかったらそのインデックス番号を返します。
ただ、よく考えてみて下さい。
var mySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('成績表'); var arrData = mySheet.getDataRange().getValues();
これで取得した二次元配列arrDataは
[ [ID, 名前, 国語, 数学,英語], [1, Noah, 52, 43, 95, ], …, [10, Georgia, 37, 68, 41 ] ]
という構成になります。
ですから、indexOfメソッドを使おうと思っても
Logger.log(arrData[0].indexOf('国語')); //2・・・見出し行から「国語」を検索 Logger.log(arrData[4].indexOf(72)); //3・・・Lucasの成績から72を検索
こんな行方向の検索しかできないのです。
いらね~って感じです。
つまり、そのままの二次元配列でindexOfメソッドを使った列方向の検索はできない、ということです。
二次元配列を行列転置する意義
そこで登場するのが行と列を入れ替える、つまり行列転置です。二次元配列arrTransが
[ [ID, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [名前, Noah, Ethan, Logan, Lucas, Hildegarde, Shane, Siever, Gauvain, Electra, Georgia], …, [英語, 95, 95, 66, 34, 64, 42, 93, 31, 77, 41] ]
となってくれているのであれば、名前がごそっと格納されている配列arrTrans[1]に対してindexOfメソッドを使うことで
Logger.log(arrTrans[1].indexOf('Lucas')); //4・・・Lucasのインデックス番号
と、名前をキーにしてインデックス番号を求めることができます。このインデックス番号は、行番号-1(配列のインデックスは0から始まるのでマイナス1)ですよね。
このように、シートデータを格納した二次元配列は列方向で操作したいときもたくさんあるのです。そして、そんな時に行と列を入れ替えることができるなら、配列操作の幅がグッと広がります。
zip.applyメソッドで行と列を入れ替える
そこで、いよいよ登場するのがUnderscore for GASのzipメソッドです。
配列の行列を入れ替えるをするには、zip.applyメソッドを使って以下のように書きます。
ちょっと?な書き方ですが、このまんまバシっと覚えちゃってください。
これだけで指定したArrayオブジェクトを行列転置した配列が取得できます。
例えば以下のようなスクリプトを実行してみましょう。
function searchSheet(){ var mySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('成績表'); var arrData = mySheet.getDataRange().getValues(); var _ = Underscore.load(); var arrTrans = _.zip.apply(_, arrData); Logger.log(arrTrans[1].indexOf('Lucas')); //4 }
実行すると、ちゃんとLucasの行番号-1、つまり「4」とログ出力されるはずです。
Underscoreのロードや、行列転置などの準備が要りますが、それを含めてもたったの三行。実際に行方向の検索についてはindexOfメソッドによるたった一行で実現できちゃいます。
まとめ
Google Apps Scriptで二次元配列の行と列を入れ替える方法をお伝えしました。
行と列を入れ替えるには、zip.applyメソッドを使いましたが、このメソッドのためだけに、Underscore for GASライブラリを使っても良いくらいの、意義と効果があると思っています。
次回以降、行と列を入れ替えるとUnderscore for GASのいくつかのメソッドが劇的に使えるようになりますので、順次お伝えしていきたいと思います。
どうぞお楽しみに!
コメント
初めまして、あいすと申します。
スクリプトを書くのはGASが初めてな初心者ですが、いつも当ブログを拝見させていただいていて大変為になっております。ありがとうございます。
そんな中で以前よりタカハシ様がお書きになったコード内にありますval,colが指し示すものが何かが理解が出来ず、最近の列行に関してもうまく理解が進んでおりませんで
もし差し支えなければval,colが指し示すものが何かご教示いただけませんでしょうか。
ちなみになのですが現在自分は列情報からの行の特定についは
getValuesで列の情報を取得してから教えていただいたindexOfを用いて特定の名前があった場合には0、無い場合には-1が出てそれをifで条件分岐をしてどの行に存在したかを判定しております。
どうやら普通はもっと別のやり方をするのですね。
あいすさん
コメントありがとうございます!
valは検索値、colは検索対象とする列番号ですね。
記事内でもリンク貼っていますが、以下記事で詳細お伝えしています。
http://tonari-it.com/gas-spreadsheet-find/
むしろそのほうがシンプルに書けるときも多いと思いますので、もちろん、その方法でも良いと思います!
御早いご返信ありがとう御座います。
情けない話ですがここまで教えて頂いたにもかかわらず
リンク先も含め直ぐ見て理解出来たとまでは行きませんでしたが
これからも当ブログを拝見させていただき徐々に理解を深めて行きたいと思います。
それから自分のやり方でもまあ大丈夫だったのですね。
回りに教えていただける者が居らず自信が有りませんでしたが少し勇気をいただけました。重ねて御礼申し上げます。