新しいことにはウェルカム

技術 | 電子工作 | ガジェット | ゲーム のメモ書き

JavaScriptのArray.reduce()をもう少しちゃんと理解する

JavaScriptのArray.reduce()はちょっとクセがあって、今まで敬遠してたのですが、理解だけはしておこうとまとめました。

Array.reduce()とは一言で言うと、配列の要素を集めて1つのアウトプットを返す関数です。

まずは基本

const arr = ["A", "B", "C"];

const ret = arr.reduce( (prev, current)=>{
    return prev + ' - ' + current;
});

console.log(ret);
// A - B - C

これはOKです。

次に、初期値を使った場合

const arr = ["A", "B", "C"];

const ret = arr.reduce( (prev, current)=>{
    return prev + ' - ' + current;
}, "START");

console.log(ret);
// START - A - B - C

これもOKです。

問題は要素が1個の場合

const arr = ["A"];

const ret = arr.reduce( (prev, current)=>{
    return prev + ' - ' + current;
});

console.log(ret);
// A

関数は前に要素がないと呼ばれません。

ただし、初期値を設定した場合は、1個目の要素の前に初期値があるので、関数は呼ばれます。

const arr = ["A"];

const ret = arr.reduce( (prev, current)=>{
    return prev + ' - ' + current;
}, "START");

console.log(ret);
// START - A

もう一つ気をつけたいのが、要素が0個の場合。 これはランタイムエラーになります。

const arr = [];

const ret = arr.reduce( (prev, current)=>{
    return prev + ' - ' + current;
});

console.log(ret);
// ランタイムエラー

要素が0個でも初期値がある場合は、想像通り大丈夫です。そして、1個で初期値が無い場合と同様、関数は呼ばれません。

const arr = [];

const ret = arr.reduce( (prev, current)=>{
    return prev + ' - ' + current;
}, "START");

console.log(ret);
// START

使用例

以上をふまえ、配列に入ったオブジェクトを、1オブジェクト1行のテキストで出力、かつ、各行のお尻には必ず改行を入れるアウトプットは、下記のような感じになります。

const arr = [
    {name:"A", id:1},
    {name:"B", id:2},
    {name:"C", id:3},
];

const ret = arr.reduce( (prev, current)=>{
    return prev + JSON.stringify(current) + "¥n";
}, "");

console.log(ret);
// {"name":"A", "id":1}
// {"name":"B", "id":2}
// {"name":"C", "id":3}
// 

Array.reduce()の挙動を知っていればそんなもんかなって感じですが、直感的で分かりやすいかと言われるとどうかなぁって感じです…。

原因は、Array.reduce()は1つ目の要素に処理を入れることはできないのに、空初期値を使って無理やり処理を入れさせているためです。

Array.reduce()だけで済まそうと横着せず、下記のように、一旦Array.map()で前処理してから使うのが本来の使い方のような気もしますね。

const arr = [
    {name:"A", id:1},
    {name:"B", id:2},
    {name:"C", id:3},
];

const ret = arr.map( (v)=>{
    return JSON.stringify(v) + "\n";
}).reduce( (prev, current)=>{
    return prev + current;
});

console.log(ret);
// {"name":"A", "id":1}
// {"name":"B", "id":2}
// {"name":"C", "id":3}
//