GASのmapとforEachで何するの？

GASでいろいろと自動実行スクリプトを書いているとき、forやwhileで回すの面倒だなと思って少し調べたらmapやforEachというのをみつけましたので、どんな動きするのかなと思い、いろいろ実験してみました。配列にしか使えないっぽいので、イテレーターの場合は変わらずwhile～hasNext()とかでまわさないといけないようですよ。


まずは、１個目。

>function listprint(){
>  var list = ["a", "b", "c", "d", "e", "f"];
>  Logger.log(list);
>}

はい。単純に配列list作ってログに出してるだけです。
で、ログがこちら

>Stackdriver のログ
>2020/06/26 18:43:07	情報	[a, b, c, d, e, f]

まぁ、あえて書くことでもないって感じですが、これがベースってことで。


次に２個目。mapとやらの書き方と実行結果を見てみます。どうやらmapとは何か処理してreturnすることが前提のようです。

>function listmap() {
>  var list = ["a", "b", "c", "d", "e", "f"];
>  var zlist = list.map(function(value, i, data){return value + "z";});
>  Logger.log(zlist);
>}

はじめ、value　は？　i　は？　data　は？　どこに宣言されてるの？って思いましたが、サイトに記載されている方法のまま実行します。
結果がこちら

>Stackdriver のログ
>2020/06/26 18:44:54	情報	[az, bz, cz, dz, ez, fz]

ほう。配列の要素それぞれに+"z"してますし、イメージどおりですね。
pythonの内包表記みたいなものでしょうか。そういやpythonの内包表記も中に出てくる　i　とか入れたときは特に宣言しなかったですね。

と、ここで終わりでいいかとも思うんですが、一応それぞれ何が入っているのかなと気になったので、returnの前にログ出るようにしてみました。

>function listmap() {
>  var list = ["a", "b", "c", "d", "e", "f"];
>  var zlist = list.map(function(value, i, data){
>    Logger.log(value);
>    Logger.log(i);
>    Logger.log(data);
>    return value + "z";
>    });
>  Logger.log(zlist);
>}

と、これが実行結果。

>Stackdriver のログ
>2020/06/26 18:49:25	情報	a
>2020/06/26 18:49:25	情報	0.0
>2020/06/26 18:49:25	情報	[a, b, c, d, e, f]
>2020/06/26 18:49:25	情報	b
>2020/06/26 18:49:26	情報	1.0
>2020/06/26 18:49:26	情報	[a, b, c, d, e, f]
>2020/06/26 18:49:26	情報	c
>2020/06/26 18:49:26	情報	2.0
>2020/06/26 18:49:26	情報	[a, b, c, d, e, f]
>2020/06/26 18:49:26	情報	d
>2020/06/26 18:49:26	情報	3.0
>2020/06/26 18:49:26	情報	[a, b, c, d, e, f]
>2020/06/26 18:49:26	情報	e
>2020/06/26 18:49:26	情報	4.0
>2020/06/26 18:49:26	情報	[a, b, c, d, e, f]
>2020/06/26 18:49:26	情報	f
>2020/06/26 18:49:26	情報	5.0
>2020/06/26 18:49:26	情報	[a, b, c, d, e, f]
>2020/06/26 18:49:26	情報	[az, bz, cz, dz, ez, fz]

ちゃんと6回まわっているんですね。
1回目　value = a / i = 0.0 / data = [a,b,c,d,e,f]
2回目　value = b / i = 1.0 / data = [a,b,c,d,e,f]
3回目　value = c / i = 2.0 / data = [a,b,c,d,e,f]
.
.
.
最後　「az,bz,cz,dz,ez,fz」

ということで、当たり前なんだろうけどdataはいつも同じ配列で、iが要素の番号、valueが要素。
一応、returnしないとエラーになるか確認。

>function listmap() {
>  var list = ["a", "b", "c", "d", "e", "f"];
>  var zlist = list.map(function(value, i, data){
>    Logger.log(value);
>    Logger.log(i);
>    Logger.log(data);
>    //return value + "z";
>    });
>  Logger.log(zlist);
>}

結果は、

Stackdriver のログ
2020/06/29 17:03:20	情報	a
2020/06/29 17:03:20	情報	0.0
2020/06/29 17:03:20	情報	[a, b, c, d, e, f]
2020/06/29 17:03:20	情報	b
2020/06/29 17:03:20	情報	1.0
2020/06/29 17:03:20	情報	[a, b, c, d, e, f]
2020/06/29 17:03:20	情報	c
2020/06/29 17:03:20	情報	2.0
2020/06/29 17:03:20	情報	[a, b, c, d, e, f]
2020/06/29 17:03:20	情報	d
2020/06/29 17:03:20	情報	3.0
2020/06/29 17:03:20	情報	[a, b, c, d, e, f]
2020/06/29 17:03:20	情報	e
2020/06/29 17:03:20	情報	4.0
2020/06/29 17:03:20	情報	[a, b, c, d, e, f]
2020/06/29 17:03:20	情報	f
2020/06/29 17:03:20	情報	5.0
2020/06/29 17:03:20	情報	[a, b, c, d, e, f]
2020/06/29 17:03:20	情報	[null, null, null, null, null, null]

returnしていないので、最後のzlistはヌルヌルしてますね。面白いのは個数分ちゃんとnullが入っているところでしょうか。配列を加工してreturnすることが前提だからですかね。


で３個目は、forEach。

function listforeach(){
  var list = ["a", "b", "c", "d", "e", "f"];
  list.forEach(function(value){ Logger.log(value); });
}

実行結果はこれ。

Stackdriver のログ
2020/06/29 16:38:50	情報	a
2020/06/29 16:38:50	情報	b
2020/06/29 16:38:50	情報	c
2020/06/29 16:38:50	情報	d
2020/06/29 16:38:50	情報	e
2020/06/29 16:38:50	情報	f

配列の要素を順番に出してますね。
「list」の中のものがひとつづつ順番にvalueに入って、{}の中の処理をするということかな。
ここで、mapとのちがいはどうかな？と思ったので実験。

function listforeach(){
  var list = ["a", "b", "c", "d", "e", "f"];
  list.forEach(function(value){ Logger.log(value　+ "z"); });
}

結果がこれ。

Stackdriver のログ
2020/06/29 16:44:03	情報	az
2020/06/29 16:44:03	情報	bz
2020/06/29 16:44:03	情報	cz
2020/06/29 16:44:03	情報	dz
2020/06/29 16:44:03	情報	ez
2020/06/29 16:44:03	情報	fz

まぁ、Logger.logでvalue+zしてるんだから当たり前といえば当たり前の結果なんですが...。
あとvalueに入っている要素を使わなくても要素の数だけ回るんだろうなと思ったので実験。

function listforeach(){
  var list = ["a", "b", "c", "d", "e", "f"];
  var i = 0;
  list.forEach(function(value){ Logger.log(i); i++; });
}

結果は、

Stackdriver のログ
2020/06/29 16:48:35	情報	0.0
2020/06/29 16:48:35	情報	1.0
2020/06/29 16:48:35	情報	2.0
2020/06/29 16:48:35	情報	3.0
2020/06/29 16:48:35	情報	4.0
2020/06/29 16:48:35	情報	5.0

iをゼロから始めてちゃんと回数分回っているし、valueを{}内で使う必要はないようです。


では最後4個目、mapと同じようにreturnしてみましょうか。

function listforeach(){
  var list = ["a", "b", "c", "d", "e", "f"];
  var listEach = list.forEach(function(value){ return value + "z" ; });
  Logger.log(listEach);
}

結果がこちら。

Stackdriver のログ
2020/06/29 16:53:46	情報	null

なるほど。
listEach内はnullのようですね。ということは書いてもreturnされていないということか。returnの受け側がないって感じですかね。まぁ、returnするならmap使えよ。っていろんなサイトで書いてありますけど。

ということでGASでforEachとmap使った実験でした。
僕と同じようにforEachやmapってなんぞや？って人の一助になれば幸いです。