【GAS】複数の配列を結合して1次元配列・2次元配列を作る方法+filterでの検索+mapでの写像方法(map・filter・arrayConcat)

GAS
この記事は約9分で読めます。

えぇ今回はタイトルにあるように、GASでの配列処理で日頃使ったり・あまり使わなかったりするものも含めて、いろいろ詰め込んみましたw

正直言ってブログの作りとしては、記事に複数要素を詰め込みすぎてダメダメな典型なのですが、まぁ今回はコンテンツもConcatしたと思ってもらって、笑って見てやってください。(いつか真面目にリライト+分割するかもですが、今のところ半分ネタ的な作りなので予定はしておりませんw)

それではいろいろ詰め込んだもの、行ってみましょう。

スポンサーリンク
もしも_楽天

元データの紹介

今回はこんな感じのA1notationベースでデータがそれぞれのセルに値を持っているスプレッドシートを利用して遊んでいきます。

基本的にこのデータを利用していきます。まずはシンプルにgetValuesで値を取得すると2次元配列が取得できていることがわかります。これが、スプレッドシートで値を取得した場合の基本になります。

function mapFunction() {
  const datas = SpreadsheetApp.getActiveSpreadsheet().getDataRange().getValues();
  Logger.log(datas)
}

実行すると以下のように取れます。行ごとに配列になった、2次元の配列が取得できています。

紹介する内容

以下の項目をこれから紹介していきたいと思います。

配列処理関係紹介内容
  • 2次元配列を1次元配列に落とす方法
  • mapの使い方
  • map+filterの使い方
  • 1次元配列を2次元配列に戻す方法

flatten処理:2次元配列を1次元配列に落とす(次元削減する方法)

まずは、2次元配列を1次元配列に落とす方法、いわゆるflatten処理、を紹介していきます。

やることのイメージは以下のようなものです(2次元3要素の配列を1次元6要素にするパターン)

元データ

[[1,2],[3,4],[5,6]]

変更後データ

[1,2,3,4,5,6]

array.prototype.concat.applyで対応する方法

では、実際のコードです。

function mapFunction() {
  const datas = SpreadsheetApp.getActiveSpreadsheet().getDataRange().getValues();
  Logger.log(datas)

  const datas_array = Array.prototype.concat.apply([],datas)
  Logger.log(datas_array)
}

実行結果です。

期待通りに2次元配列が1次元配列に次元削減されてますね。

concat.applyの解説

さて、少しだけ何をやっていたかの解説をします。

concatメソッド:配列をつなげる処理

applyメソッド:第一引数の関数に第二引数の配列を当てる

flatメソッドがなかった時によく利用された方法ですね。

シンプルにflatメソッドを利用する方法(ES2019から適用されたメソッド)

別にもっとシンプルに今は配列自体にflatメソッドが利用可能になっているので、flatメソッドを利用して以下のように処理することも可能。

function mapFunction() {
  const datas = SpreadsheetApp.getActiveSpreadsheet().getDataRange().getValues();
  Logger.log(datas)
  
  const datas_arrya2 = datas.flat();
  Logger.log(datas_arrya2)
}

GASでこのコードを書くとflatメソッドにアラートが表示されますが、特に気にせず実行可能です。実際の画面は以下。

実行結果は以下のようになります。

flatメソッドでも綺麗に次元消滅ができています。

タイトルにも書いたようにES2019から適用されたメソッドのため比較的新しいですね。

追記:スプレッド構文を利用する方法

ノンプロ研メンバー つじけさん(@TsujiKenzo)からコメントをいただき、もう一個追加します。確かにありましたね、スプレッド構文。

実際のコードはこんな感じのやつです。”…”ってのが、スプレッド構文ですね。処理のイメージとしては、アンパックをイメージしてもらえればいいと思います。

const datas_array3 = [...datas[0],...datas[1],...datas[2]]
Logger.log(datas_array3)

2次元配列のアンパックなんですが、一括で全てアンパックしてくれるわけではないので、一個づつ要素指定する必要があるかも。(ちょっと私が詳しくないので、一括アンパックのスプレッド構文もあるかもしれません)

もちろん上記のスクリプトでも前2つの方法と同様の結果になります。

mapの使い方

では次はmapメソッドを利用していきましょう。

mapメソッド: 写像。与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を生成

mapメソッドを利用するサンプルは以下のようなものです。アロー関数もセットになっていますが、あしからず。

var data_map = Array.prototype.concat.apply([],datas).map(value => value+value);
Logger.log(data_map)

実行結果は以下のようになります。datasの配列要素全てが文字重複して表示されていることを確認ください。

イメージとしては、配列全体に特定の処理を(今回の場合はvalue+value)実行して、新たな配列を返します。

flatMap()を使ってみると?

ES2019から適用されたメソッドにflatMap()というのがあります。名前だけみるとflatさせてmapしてくれそうですよね?

試してみましょう。

var data_map2 = datas.flatMap(value => value + value);
Logger.log(data_map2)

実行結果は?

残念がら期待通りとはいきません。なぜなら。。。

flatMapメソッド:最初にマッピング関数を使用してそれぞれの要素をマップした後、結果を新しい配列内にフラット化

そうなんです、最初にマッピングしてからフラット化するので、順番が逆でマッピングが先に実行されるので期待される結果には残念ながらなりません。

flatメソッド+mapメソッド

順番が逆なら分割して使えばもちろん対応可能です。

var data_map3 = datas.flat().map(value => value +value);
Logger.log(data_map3)

実行結果はやってみてもらえればわかると思いますが、一番最初に実行したmapメソッドの結果と同じです。

まぁconcat.applyの部分をflatに置き換えているだけなので、当然といえば当然ですねw

map+filterの使い方(データ検索)

次は、mapとfilterを併用して1次元配列にした要素から抽出をしていきます。

今回のケースでは正規表現を利用して検索していきます。正規表現忘れてしまった方は、後ほど関連記事のリンクを付けていきますので、そちらもご確認ください。

まずは実際のコードから。

var regEXP = /A[1-3]/;
var data_filter = Array.prototype.concat.apply([], datas).filter(value => value.match(regEXP));
Logger.log(data_filter)

実行結果は以下の感じです。

無事フィルタリングにマッチしたものだけ、配列に保存されています。

参考:正規表現の解説記事

正規表現忘れてしまったよ、って人はこちらの解説記事もどうぞ。

1次元配列を2次元配列に戻す方法

さて、最後はまぁ使わないかなと思われるメソッドですが、1次元配列を2次元配列に戻す方法の紹介です。

array.pushで少しずつ足していくって方法もあるのでしょうが、一括でドバッとやりたい性格の私としては、以下のようなコードで対応しています。

 const sliceByNum = (array, number) => {
    const len =  Math.ceil(array.length / number);
    return new Array(len).fill().map((_, i) => array.slice(i * number, (i + 1) * number))
  };

 Logger.log(sliceByNum(data_map,3));

ちょっと何やってんだ?ってなるかもしれないコードですが、実行すると以下のようになります。

3つの要素を含んだ配列を1次元配列として、2次元配列を組んでます。

そんな難しいことはしていないのですが、一点補足しておかないと自分も最初の頃処理できなかった部分を解説します。

なぜfill()がいるの?

returnで設定しているnew Array(len).fill().map(…..の部分ですが、パッとみfillメソッドいらなそうに思えませんか?私も最初いらないだろと思ってArray(len).map(…って書いて失敗していますw 同じこと考えた方類友ですね^ ^

なぜfillメソッドが必要なのか、というところに関しては、以下の理解が非常に大事になってきます。

Javascriptの配列は、実質的には数値をキーとしたオブジェクトである

そうなんです、あくまでobject何ですよ、かつ数値がキーになっている。これってどういうことかというと、シンプルには以下のような感じです。developerツールで試した結果です。

ここで、fillメソッドがない状態のnew Array()ってどんな状態かっていうと、上の図で言うlengthだけあって、objectの中身が何もない状態になるんですね。実行してみます。

だから、ここに無理やり要素をぶち込んでやるためにfillメソッドが必要と言うことです。

いやぁ実は自分で全部調べたのか、と言われると昔以下の記事に当たっただけなんですけどねw 英語ですが、参考になるのでリンクしておきます。

Here's Why Mapping a Constructed Array Doesn't Work in JavaScript
And How To Do It Correctly

まとめ

今回は、【【GAS】複数の配列を結合して1次元配列・2次元配列を作る方法+filterでの検索+mapでの写像方法(map・filter・arrayConcat)】と言うことで、要素盛り沢山で解説少なめでお届けしました。

flatメソッドは非常に便利ですし、mapやfilterは活用されている方も多いと思います。

他方で、最後に紹介した2次元配列を作成するスクリプトは結構綺麗かなぁと思いつつも、あまり流通してないイメージ。GASで使うケースが少ないからかもしれませんね。

ちょっと、もりもりで内容も変わった感じのものを取り揃えてみましたが、pythonもGASもいろいろなメソッドに触れると原子的要素の理解促進に繋がるのでいいと個人的には思います。

年始も少したちましたが、今年も継続してGAS使っていきましょう。

タイトルとURLをコピーしました