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

#JavaScript の配列には #コールバック を抱えて走ってくれるメソッドがあります。どうやら処理が早いようなので、積極的な利用が肝要です。
今回は #reduce #reduceRight の紹介です。(これでこのシリーズは終わりと思っていたら、まだ #find と #findindexという #ES6 からの新参者がありましたね。)
いままで #forEach #map 、 #filter #some 、 #every を紹介してまいりましたが、個人としてはreduceとreduceRightに最も恩恵を感じてます。

よくある解説では、reduce(callback [, initialValue])とあって、 #callback の中身はreduse(function(前回処理結果, 現要素, 位置, 配列) {}, 初期値)と表記されます。「前回処理結果」は繰り返し処理される前回の結果の返り値、「要素」は繰り返し処理される配列の一要素、「位置」はその要素の配列内のインデックス、「配列」は処理される配列そのものです。「初期値」は初回の前回処理結果に代入する値です。reduceRightの記述も全く同じです。

動作をざっくりと申し上げれば「配列の要素を処理し、取得した返り値を次の処理に利用して順繰りに最終的な返り値を取得して出力する。」でしょうか。いままで紹介したコールバックタイプメソッドと比べると複雑なのですが、返り値の扱いは単純になります。

それでは、よく紹介されている利用方法で配列(数値)内の最大値を取得してみます。「JavaScriptで配列の最大値・最小値求めるならreduceで」など多くの記事が参考になります。(感謝。)とりあえずは、よくある感じで記述します。
つづくターミナル表示は、macOS上でSafariでのコンソール出力の表記です。また、「>」がコンソールへ私が打ち込んだ入力テキストで、「<」がリターンキーを押すと現れる出力情報です。

> ages = [21,112,13,44,15,60,79,18];
< [21, 112, 13, 44, 15, 60, 79, 18] (8)

数値で構成される配列を作りました。それでは最大値を取得してみます。

> ages.reduce((pv,cv,id,ar)=>(pv > cv)? pv : cv);
< 112

ちゃんと最大値の112が返ってきました。まずは、どういった動作をしているかログを取って見てみましょう。(因みに、pvは”Previous value”、cvは”Current valu”、arは”Array”の略(hustlemouse.com流)で、idは”id”まんまです。)

> ages.reduce((pv,cv,id,ar)=>{
	console.log(`PV_>${pv}_CV_>${cv}_ID_>${id}_AR_>${ar}`);
	return (pv > cv)? pv : cv;
});
[Log] PV_>21_CV_>112_ID_>1_AR_>21,112,13,44,15,60,79,18
[Log] PV_>112_CV_>13_ID_>2_AR_>21,112,13,44,15,60,79,18
[Log] PV_>112_CV_>44_ID_>3_AR_>21,112,13,44,15,60,79,18
[Log] PV_>112_CV_>15_ID_>4_AR_>21,112,13,44,15,60,79,18
[Log] PV_>112_CV_>60_ID_>5_AR_>21,112,13,44,15,60,79,18
[Log] PV_>112_CV_>79_ID_>6_AR_>21,112,13,44,15,60,79,18
[Log] PV_>112_CV_>18_ID_>7_AR_>21,112,13,44,15,60,79,18
< 112

再確認です。PVに入るのは前回処理の返り値です。returnがなければ成り立たないことをよく認識しておいてください。
ages配列の要素数(length)は8個ですが、ログの回数は7回で1回足りません。IDの値に0番も見当たりませんね。最初のログのPVとCVの値から判るように、初回には前回処理の結果が存在しないのでPVに初端要素(idが0番)を利用して、実質的には2回目から処理が始まっているのです。処理全体の返り値は、最後の処理の返り値でもある112です。
試しに、初期値として200を割り当ててみます。

> ages.reduce((pv,cv,id,ar)=>{
	console.log(`PV_>${pv}_CV_>${cv}_ID_>${id}_AR_>${ar}`);
	return (pv > cv)? pv : cv;
}, 200);
[Log] PV_>200_CV_>21_ID_>0_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>112_ID_>1_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>13_ID_>2_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>44_ID_>3_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>15_ID_>4_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>60_ID_>5_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>79_ID_>6_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>18_ID_>7_AR_>21,112,13,44,15,60,79,18
< 200

今度はログが8回あります。初っ端にPVには200が入ってidの0番から処理が始まり、配列の中には200より大きな数値はないことから最後まで入りっぱなしになりました。処理全体の返り値も200のままです。それではreduceRightも試してみましょう。

> ages.reduceRight((pv,cv,id,ar)=>{
	console.log(`PV_>${pv}_CV_>${cv}_ID_>${id}_AR_>${ar}`);
	return (pv > cv)? pv : cv;
}, 200);
[Log] PV_>200_CV_>18_ID_>7_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>79_ID_>6_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>60_ID_>5_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>15_ID_>4_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>44_ID_>3_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>13_ID_>2_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>112_ID_>1_AR_>21,112,13,44,15,60,79,18
[Log] PV_>200_CV_>21_ID_>0_AR_>21,112,13,44,15,60,79,18
< 200

前後(reduceRightだから左右ですか。)がひっくり返っただけで、同じ処理すね。

最大値の取得は #Math.max() でもできるそうですが、reduceがお勧めだそうです。参考として次の記事などもさらっとご覧になると良いでしょう。(感謝。)
【JavaScript】配列の最大値(最小値)を取得するには reduce を使うのがいいらしい
Math.max() – JavaScript | MDN

> ages
< [21, 112, 13, 44, 15, 60, 79, 18] (8)

もとのテーブルを参照して、これらメソッドが #非破壊 であることを確認できます。

最大値最小値などだけでなく、返り値を利用した複雑な処理も可能ではないかと考えられます。でも個人的には、まだ最大値最小値の複合バリエーションぐらい(でも大活躍なんです。)にしか利用してませんね。期待ばかりが膨らみます。

ところで、ここで”Previous value”の略でpvとしていたパラメーターを「Array.prototype.reduce() – JavaScript | MDN」では「 #アキュームレーター ( #accumulator )」と表現してます。辞書では「蓄財家」とか「演算の結果が格納されるレジスタの一種」とありましたが、「勝ち金を自動的に繰り越す賭けの方式」ともあり再投資型や複利式の金融資産を思い浮かべました。
本来なら”accumulator”の略でacとかで良いのかもしれませんが、機能に貯めるという印象を感じなかったのでざっくりと「前回の値」の略でpvとしてます。

うーむ。 #バイシクルオペランド (略してbo)なんてどうですかね?身に染みているのか自転車操業だったらしっくりするのです。そかさ。