配列オブジェクトのコールバックタイプメソッド(その1)

#JavaScript の配列には #コールバック を抱えて走ってくれるメソッドがあります。 #map とか #some #every #filter #forEach #reduse #reduseRight なんてたくさんあって、「どれがなんじゃとお!」と思いながら行き当たりばったりに使っていました。そしたら #ES6(ES2015) から、 #find と #findeindex なんてお仲間が増えてました。「余計なことを…。」とか愚痴が脳裏を掠めますが、ちゃんと理解して便利に使い倒して差し上げようと希望します。なわけで、ちょっとだけ調査と自分なりに整理して、ざっくりと備忘のめたに投稿をいたします。(数回の連載にしたいと自分に期待してます。)

実は、 #sort も地味?に関数を利用できるのですが、とりま、よくコールバックタイプとして区分されているものを取り上げます。

まずは下準備に、動作確認用に #多次元配列 ‘tbl’を作ります。コールバックの動作を明示的にするため、この連載を通して利用したいと考えます。(特にfilterでは顕著かと…、)
実用性も考え、 #MIME のテーブルを作ります。つづくターミナル表示は、macOS上でSafariでのコンソール出力の表記です。また、「>」がコンソールへ私が打ち込んだ入力テキストで、「<」がリターンキーを押すと現れる出力情報です。

> tbl = new Array(
	['text/plain','txt'],
	['text/csv','csv'],
	['text/tsv','tsv'],
	['text/css','css'],
	['text/javascript','js'],
	['text/xml','xml'],
	['text/html','html']);
< [["text/plain", "txt"], ["text/csv", "csv"], ["text/tsv", "tsv"], ["text/css", "css"], ["text/javascript", "js"], ["text/xml", "xml"], ["text/html", "html"]] (7)

今回は、あまり特殊でない?ところのforEachを取り上げます。これはコールバックのお仲間で紹介されるのですが配列オブジェクトの #イテレーター 機能をワンフレーズで利用できるってだけのものです。ですから、どっちかというと「for(let t of tbl) { }」と一緒に紹介したほうが理解しやすいぐらいかと思っています。ともかく、for文と同じくクルクル回るだけですから、メソッドから直接の返り値は期待できません。まあ、実際の利用では同じことなのですが、コンソールで結果を得るためには変数を準備しておく必要があります。

> fe = new Array();
< [] (0)
> tbl.forEach((vc,id,ar)=>fe.push(vc[0]));
< undefined
> fe
< ["text/plain", "text/csv", "text/tsv", "text/css", "text/javascript", "text/xml", "text/html"] (7)

最初に結果を得るために’fe’という名前の配列を生成してます。
つぎに’tbl’多次元配列のforEachを参照するのですが、先につぎの行をご覧ください。undefinedになってるでしょ?なんも返してくれません。(と理解してます。)
それでforEachの中身ですが、ここがコールバックタイプメソッドの基本です。
よくある解説では、forEach(callback [, that])とあります。そして「thatパラメータは関数callback内でthisを示すオブジェクト」という旨の記述があります。必要に応じて付加するのですが、個人的にはアロー関数に慣れ親しんでいてすっかり忘れてました。
そして御本尊のcallbackは、forEach(function(要素, 位置, 配列) {処理})と表記されます。「要素」は繰り返し処理される配列の対象要素、「位置」はその要素の配列内のインデックス、「配列」は処理される配列そのものです。判りやすいようにシンプルな処理でログ出力します。

> data = [2, 3, 4, 5];
data.forEach(function(vl, id, ar) {
	//'vl'は配列要素の値(VALUE)
	//'id'はインデックス番号(INDEX)
	//'ar'は'data'配列(ARRAY)
	console.log(`各要素の値を累乗する_INDEX_>${id}_VALUE_>${vl}_ARRAY_>[${ar}]_CALCULATED_>${vl*vl}`);
});
[Log] 各要素の値を累乗する_INDEX_>0_VALUE_>2_ARRAY_>[2,3,4,5]_CALCULATED_>4
[Log] 各要素の値を累乗する_INDEX_>1_VALUE_>3_ARRAY_>[2,3,4,5]_CALCULATED_>9
[Log] 各要素の値を累乗する_INDEX_>2_VALUE_>4_ARRAY_>[2,3,4,5]_CALCULATED_>16
[Log] 各要素の値を累乗する_INDEX_>3_VALUE_>5_ARRAY_>[2,3,4,5]_CALCULATED_>25
< undefined

よろしいでしょうか?では、はなしを元に戻し、前者のアロー関数の部分です。

(vc,id,ar)=>fe.push(vc[0])

パラメータは同じく(要素, 位置, 配列)です。アローのうしろは、用意の’fe’配列に’tbl’多次元配列の対象要素IDゼロ番にあるMIME文字列だけ格納してます。これを配列要素毎に繰り返して処理します。undefinedの後で’fe’を参照してみると、MIMEだけの配列になっていることが確認できます。さらに、もう少し複雑な処理をしますね。アロー関数の後ろに条件文を付加します。中括弧でラップします。

> fe = new Array()
< [] (0)
> tbl.forEach((vc,id,ar)=>{if(vc[1].length===3)fe.push(vc[0])});
< undefined
> fe
< ["text/plain", "text/csv", "text/tsv", "text/css", "text/xml"] (5)

‘tbl’多次元配列の対象要素ID2番にある拡張子文字列が3文字だったら対象要素IDゼロ番にあるMIME文字列を格納するようにしました。これを繰り返し処理してます。比較的にシンプルな記述でちょっとした処理を行えることがわかります。

そして、forEachでいろいろと処理をしても元の’tbl’多次元配列に変化はありません。 #非破壊 であることを確認してください。

> tbl
< [["text/plain", "txt"], ["text/csv", "csv"], ["text/tsv", "tsv"], ["text/css", "css"], ["text/javascript", "js"], ["text/xml", "xml"], ["text/html", "html"]] (7)

無事ですね。

配列オブジェクトのforEachメソッドのあらましでした。

この調子でfilterやらmapやらをやっつけていきたいです。
ところで、forEachは配列ライクな(たぶんイテレータタイプのオブジェクト)であれば、以前投稿した #NodeList などでもノードの階層を掘ったりする用途で便利に使えます。でも、NodListなどが配列のように扱えるからといって、他のfilterとかはできませんでした。期待して挑戦したのですがハネられ、徒労に終わりました。とほほ。(そかさ。)