JavaScriptで配列の合計から値を均等に減算し、結果を非負に保つ方法
整数の配列と、その配列の合計から差し引きたい値が与えられたとします。
たとえば、次のようなケースです。
[4, 5, 6, 7, 8] − 25
全要素に25を均等に割り振って減算すると、結果は次のようになります。
[−1, 0, 1, 2, 3]
しかし今回は、結果に0未満の値を含めたくないという条件があります。
この条件を満たすアルゴリズムを考えると、マイナスになってしまった分は、残りのプラスの値に対して均等に振り替える必要があります。まず先頭の要素が0になり、減算しきれなかった不足分が後続の要素へ引き継がれます。
[0, 0, 1, 2, 3] − 1
この不足分1を残りの3つの要素で均等に負担すると、最終的な配列は次のようになります。
[0, 0, 1 − 0.333, 2 − 0.333, 3 − 0.333]
ここでのポイントは、負の値がすべて残りの正の値へ「均等に」オーバーフローするという点です。まさにこれこそが、私たちの求めている結果です。
要件
今回実装するのは、次の仕様を満たすJavaScript関数です。
- 第1引数:減算対象となる数値の配列を受け取る
- 第2引数:合計から差し引く値を受け取る
- 均等に減算・分配された新しい配列を計算して返す
サンプルコード
const arr = [4, 5, 6, 7, 8];
const subtract = (arr, sum) => {
return arr.map((el, index, array) => {
const rem = array.length - index;
const avg = sum / rem;
const toSubtract = Math.min(avg, el);
sum -= toSubtract;
return el - toSubtract;
});
};
console.log(subtract(arr, 25));
出力結果
コンソールには次のように表示されます。
[ 0, 0, 0.666666666666667, 1.666666666666666, 2.666666666666666 ]
アルゴリズムの解説
この実装では、map()メソッドを使って配列を先頭から順に処理していきます。各ステップの動作は次のとおりです。
- rem(残り要素数):現在の位置から配列末尾までの要素数。
- avg(平均減算額):まだ減算しきれていない合計額(sum)をremで割った、1要素あたりの減算量。
- Math.min(avg, el):要素自身の値を上限として減算します。要素がavgより小さければ、その要素はちょうど0になります。
減算しきれなかった分は変数sumに繰り越され、以降の要素へ均等に再分配されます。この仕組みにより、どの要素も0未満になることなく、減算総額がすべての要素に公平に分散されるのです。
-
JavaScriptにおける配列とSetの違いとは?重複値の扱いと要素アクセスをわかりやすく解説
JavaScriptのSet(セット)オブジェクトは、ES2015(ES6)で新たに導入されたデータ型です。従来から広く使われている配列(Array)と比べると、いくつか重要な違いがあります。 配列とSetの主な違い 重複した値の扱い: 配列は同じ値をいくつでも格納できますが、Setでは同一の値は1つしか保持されません。重複した値は自動的に除外されるのが大きな特徴です。 要素へのアクセス方法: 配列ではインデックス(例:arr[0])を使って任意の位置の要素へ直接アクセスできます。一方、Setにはインデックスの概念がなく、要素は挿入された順序どおりに先頭から順番に辿ることしかできません。
-
JavaScriptで配列をセット(Set)に変換する方法|new Set()の使い方を解説
JavaScriptでは、new Set() コンストラクタを使うことで、配列を簡単にセット(Setオブジェクト)へ変換できます。セットは同じ値を自動的に1つだけ保持する性質を持つため、配列から重複した要素を取り除きたい場合にも非常に便利です。 配列をセットに変換するサンプルコード 以下は、ボタンをクリックすると配列がセットに変換されて表示される、完全なHTMLサンプルです。 <!DOCTYPE html> <html lang=ja> <head> <meta charset=UTF-8 /> <meta name=viewport co