【JavaScript】配列の合計 / 平均 / 中央値を求めるには？

# 合計
コードはこちらです。

```js
/**
 * 配列の合計を求める関数
 * @param {number[]} array 
 * @returns {number} 合計
 */
function sum(array) {
  return array.reduce((acc, cur) => acc + cur, 0)
}
```

```js:呼び出し例
sum([3, 6]) // 9
sum([1, 3, 5, 2]) // 11
```

このコードでは、配列の`reduce`メソッドを用いて合計を計算しています。

## `reduce`とは
配列の[`reduce`](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)は、配列の要素を**縮小**するメソッドです。

縮小には指定したコールバック関数が使われます。
コールバックは各要素ごとに順番に実行され、返した値が次のループに使われます。
以下の引数で呼び出されます。

1. `accumulator`: 前回のコールバック関数が返した値
1. `currentValue`: 現在のループの要素
1. `currentIndex`: 現在のループのインデックス
1. `array`: 呼び出されている配列そのもの

例えば、コールバック関数で`accmulator`と`currentValue`を足し、それを返すようにします。
すると簡単に配列の合計が計算できます。

```js
const array = [1, 3, 5]

const sum = array.reduce((acc, cur) => {
  return acc + cur // 合計を返す
})

sum // 9
```

### 初期値
コールバック関数が一番最初に呼び出されるとき、`accmulator`は配列の一番最初の要素になります。
そのため`currentValue`は配列の`1`番目（`0`ではない）の要素が最初に入ります。

```js
const array = [1, 3, 5]
array.reduce((acc, cur) => { // 1と3が最初に入る
  return acc + cur
})
```

そして、呼び出された配列の中身が空（`[]`）だった場合、`reduce`はエラーを投げます。

```js
const array = []
array.reduce((acc, cur) => acc + cur) // Error!
```

今回の`sum`関数は配列が`[]`でもエラーではなく`0`を返したいので、これは問題です。

そこで、第二引数の`initialValue`が使えます。
これは`accmulator`の初期値を設定するもので、これを入れると配列が`[]`でもエラーを投げなくなります。

今回は初期値に数値の`0`を設定します。

```js
const array = []
array.reduce((acc, cur) => acc + cur, 0) // 0
```

:::note
もちろん、`array.length`をチェックするなどして配列が空であることを確認してもOKです。

```js
function sum(array) {
  if (array.length <= 0) return 0
  return array.reduce((acc, cur) => acc + cur)
}
```

個人的には初期値を設定したほうがすっきりするので好きです。

```js
function sum(array) {
  return array.reduce((acc, cur) => acc + cur, 0)
}
```
:::

## `for...of`の場合
先ほどのコードは、以下のように`for...of`文で書くこともできます。

```js
function sum(array) {
  let result = 0
  for (const num of array) {
    result += num
  }
  return result
}
```

```js:呼び出し例
sum([3, 6]) // 9
sum([1, 3, 5, 2]) // 11
```

このコードでは合計を`result`変数で管理し、配列の要素を順番に足していっています。
そして計算し終わったら`result`を返しています。

# 平均

コードはこちらです。
```js
/**
 * 配列の平均を求める関数
 * @param {number[]} array 
 * @returns {number} 平均
 */
function average(array) {
  const sum = array.reduce((acc, cur) => acc + cur, 0)
  return sum / array.length
}
```

```js:呼び出し例
average([3, 5]) // 4
average([5, 8, 10, 2, 16]) // 8.2
```

## 求め方
平均は以下の手順で求められます。

1. 合計（`sum`）を計算する
1. `sum`を要素数（`array.length`）で割る

`average`関数は、先ほどと同じ方法で合計を計算し、それを`array.length`で割ったものを返しています。

# 中央値

コードはこちらです。
```js
/**
 * 配列の中央値を求める関数
 * @param {number[]} array
 * @returns {number} 中央値
 */
function median(array: number[]) {
  const sorted = array.toSorted((a, b) => a - b)
  const centerIndex = sorted.length / 2

  // 真ん中のインデックスが小数か？ = array.lengthが奇数か？
  if (Number.isInteger(centerIndex)) {
    // 偶数 中央に近い2つの数の平均を返す
    const a = sorted[centerIndex - 1]
    const b = sorted[centerIndex]
    return (a + b) / 2
  } else {
    // 奇数: インデックスを切り捨てて返す
    return sorted[Math.floor(centerIndex)]
  };
}
```

## 求め方
中央値は以下の手順で求めることができます。

1. 配列の要素をソートする（`sorted`）
1. 真ん中のインデックス（`sorted.length`）が小数かを判定する
1. 小数の場合: 切り捨てた位置にある値を返す
1. 整数の場合: 真ん中に近い2つの値の平均を返す

## 配列の要素をソートする

ソートには配列の`toSorted`メソッドを使います。
このメソッドは`sort`に似ていて、ソートした新しい配列を返します。

```js
array.toSorted((a, b) => a - b)
```

引数に入れた関数（`compareFn`）は、どのようにソートするかを示すものです。
ここでの`a - b`は数値を昇順でソートします。

なお、ここでは`compareFn`の戻り値が何を示すのかわからなくても大丈夫です。
中央値を求めるにあたって、ソートが昇順でも降順でも結果は変わりません。

`toSorted`の詳細は[MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted)をご覧ください。

## 真ん中のインデックスが小数か判定する

まず、以下のコードでは、この後何度も出てくる真ん中のインデックスに名前をつけています。
これは単純に`sorted.length`を`2`で割ったものです。

```js
const centerIndex = sorted.length / 2
```

`centerIndex`の値は以下のようになります。

- `sorted.length`が偶数の場合: 整数
- `sorted.length`が奇数の場合: `~.5`という形の小数

また、中央値は数値の数が偶数か奇数かで少し求め方が変わります。

- 偶数の場合: 真ん中に最も近い2つの数の**平均**になる
- 奇数の場合: 真ん中の数になる

このため、`currentIndex`が小数かどうかを判定し、そこから条件分岐で処理を変える必要があります。

### 判定方法
数が整数かどうか判定するには、[`Number.isInteger`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger)が使えます。

```js
if (Number.isInteger(centerIndex) {
  // 整数の場合
} else {
  // 小数の場合
};
```

## 整数の場合
整数の場合=配列の長さが偶数の場合、以下の値を返します。

1. 中央に近い小さいほうのインデックス（`currentIndex - 1`）の値を取得する
1. 中央に近い大きいほうのインデックス（`currentIndex`）の値を取得する
1. 1と2の値の平均を返す

コードだとこの部分になります。

```js
const a = sorted[centerIndex - 1]
const b = sorted[centerIndex]
return (a + b) / 2
```

## 小数の場合
小数の場合、以下の値を返します。

1. `centerIndex`を**切り捨てる**
1. 1のインデックスの値を返す

コードだとこの部分になります。

```js
return sorted[Math.floor(centerIndex)]
```

ここで切り捨てている（[`Math.floor`](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Math/floor)を使っている）理由は、配列のインデックスが`0`から始まるからです。
例えば、インデックス`0 ~ 2`の中央は、`3 / 2 = 1.5`を切り捨てて`1`になります。

<!--
ネタ思いっきり被ってた...気づかなかった...

-->