【JavaScript】 配列総和(合計)を求める関数がないので自作する
更新日:2020/12/18
JavaScriptで配列要素の合計を求めようと思ってメソッドを探したのですが、標準関数として用意されていませんでした。
どうやら自作するしかないようです。
そこで今回は、配列要素の合計を求める関数を自作してみます。
完成形
今回は引数として配列だけではなくて、数値の羅列でも総和が求められる関数を作成しました。
配列要素の合計を求める関数 sum()
const sum =
(...data) => ( Array.isArray(data[0]) ? data[0] : data )
.reduce( ( a ,b ) => a + b , 0);
配列を引数として指定する例です。
実行例
const arry = [ 5, 2, 4, 3];
console.log( sum( arry ) ); // 結果:14
数値の羅列を引数として指定する例です。
実行例
console.log( sum( 1, 5, 10 ) ); // 結果:16
次は、引数として複数の配列と数値を指定できるパターンです。
配列要素の合計を求める関数:別パターン
const sum2 =
(...data) => data.reduce(
( a ,b ) => a +
( Array.isArray(b) ? b.reduce( (b1 , b2 ) => b1+b2) : b )
, 0);
実行例
const arry1 = [ 1, 2, 3];
const arry2 = [ 4, 5, 6];
console.log( sum2( arry1, 10, arry2 ) ); // 31
解説
上の項目で最初に挙げたsum関数の解説をしてみます。
わかりやすいように、同じものを再掲しておきます。
配列要素の合計を求める関数 sum()
const sum =
(...data) => ( Array.isArray(data[0]) ? data[0] : data )
.reduce( ( a ,b ) => a + b , 0);
配列要素の合計はreduceを使用する
配列内の要素を合計する場合、直感的には次のようなArray.prototype.forEachで配列から要素を一つずつとり出して合計するコードが考えられます。
配列内の要素を合計:forEach()を使用
const sum = data => {
let goukei = 0;
data.forEach( e => goukei += e );
return goukei;
};
const arry = [ 5, 2, 4, 3];
console.log( sum( arry ) ); // 14
これでも特に問題はありません。
しかしArray.prototype.reduce()を使うと、もっと簡潔に書くことができます。
配列内の要素を合計:reduce()を使用
const sum = data => data.reduce( ( a ,b ) => a + b , 0);
const arry = [ 5, 2, 4, 3];
console.log( sum( arry ) ); // 14
reduce()の2番目の引数は計算の初期値です。
1番目の引数はコールバック関数です。
コールバック関数1番目の引数は初期値を、2番目の引数は配列要素が順番に渡されます。
そしてコールバック関数のリターン値が、次の要素のコールバック関数呼び出しの初期値として渡されます。
つまり、上の例では次のような計算が行われています。
コールバック関数1回目: 0 + 5 = 5
コールバック関数2回目: 5 + 2 = 7
コールバック関数3回目: 7 + 4 = 11
コールバック関数4回目: 11 + 3 = 14
この結果、reduce()のリターン値は14になります。
ちなみにreduce()の2番目の引数は省略可能です。
省略した場合は、1回目のコールバック関数呼び出しで1番目の引数に配列の0番目の要素を、2番目の引数に配列の1番目の要素が渡されます。
引数に配列または数値の羅列指定可能にする
次に引数として配列または数値の羅列指定を可能にする仕組みを説明してみます。
アロー関数定義の引数定義を見ると、次のようになっています。
( ...data )
この ... は、レスト構文で、関数呼び出し時に羅列された引数を配列にまとめることができます。
例えば次のようにsum()関数を呼び出すとします。
sum( 1 , 2 , 3 );
すると、受け取った側では次のように配列にまとめられます。
data : [ 1 , 2 , 3]
配列を引数として指定した場合は、次のようになります。
sum( [ 1 , 2 , 3 ] );
→ data : [ [ 1 , 2 , 3] ]
わかりにくいですが、dataの0番目の要素に、配列[ 1 , 2 , 3] がセットされています。
次に、コード本体では3項演算子が使用されています。
( Array.isArray(data[0]) ? data[0] : data )
これは、配列dataの0番目の要素が配列だったらその要素を、配列でなければdataを後に続くreduce()の計算対象としています。
数値かどうかのチェック
今回挙げたコードは、データとして数値が与えられることが前提となっています。
しかし文字列などを指定しても、特にエラーにはなりません。
数値以外を指定した場合
console.log( sum( 5, "10", "abc", 4, {} , undefined) );
// 結果: 510abc4[object Object]undefined
オブジェクト( {} )は、計算時文字列"[object Object]"に変換されます。
そのため、次のような流れで合計演算がおこなわれます。
1回目: 0 + 5 = 5 ← 数値
2回目: 5 + "10" = "510" ← 文字列
3回目: "510" + "abc" = "510abc" ← 文字列
4回目: "510abc" + 4 = "510abc4" ← 文字列
5回目: "510abc4" + "[object Object]" = "510abc4[object Object]" ← 文字列
6回目: "510abc4[object Object]" + "undefined" = "510abc4[object Object]undefined" ← 文字列
計算結果のチェック
上の結果が許容できない場合、関数の結果をチェックする必要があります。
数値かどうかのチェック
const isNumber = n => typeof n === "number" || n instanceof Number;
console.log( isNumber( sum(1 , 5 , 10) ) ); // true
console.log( isNumber(sum(5,"10","abc",4,"def",{},undefined)) ); // false
数値かどうかをチェックする関数にisNaNがあります。
isNaNは数値でないときにtrueを返しますが、"123"などの数値文字列も123などの数値と同じ結果を返します。
JavaScriptは数値と文字列を足し算すると、文字列として結合されます。
例:5 + "123" + "5" = "51235"
結果は数値ですが、期待した合計値になっていません。
そのため、ここではisNaNは使用できません。
そこでtypeofやinstanceofを使用して数値判断をしています。
関数内でチェック
実際の運用としては、次のように関数内でエラーチェックを行った方が手間が省けます。
結果の確認を関数内でおこなう
const sum = (...data) => {
const goukei =( Array.isArray(data[0]) ? data[0] : data )
.reduce( ( a ,b ) => a + b , 0);
if( !isNumber(goukei) ) throw( new Error("数値以外が含まれている!") );
return goukei;
};
データ数が多い可能性がある場合、要素ごとにチェックしたほうがよいケースもあります。
要素ごとに型チェック
const sum = (...data) => ( Array.isArray(data[0]) ? data[0] : data )
.reduce( ( a ,b ) => {
if( !isNumber(b) )
throw( new Error("数値以外が含まれている!:" + b) );
return a + b;
}, 0);
数値以外は0として扱うケースもありますね。
数値以外は0として扱う
const sum = (...data) => ( Array.isArray(data[0]) ? data[0] : data )
.reduce( ( a ,b ) => a + (isNumber(b) ? b : 0 )
, 0);
どのパターンがよいのかは、目的とするプログラムによって異なります。
よく検討する必要がありますね。
更新日:2020/12/18
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。