Array.prototype.reduce()を使いこなしたい

# reduce()とは

`Array.prototype.reduce() `

配列からひとつの値を求めるときに使われる。
いろんなこと出来そう、便利そう、面白そう、、、と思ったので、理解を深めるためにメモに残す。

## 構文

1. accumulator(前回の値)
2. currentValue（現在の値）
3. currentIndex（現在currentとして処理されている要素のindex）
4. array(現在操作されている配列)

initialValueは第２引数で初期値がはいる。

```js
const result = array.reduce((accumulator, currentValue, currentIndex, array) => {
  return accumulator
}, initialValue)
```

### 例

arrayの数字を足し上げて、合計値を得る。
初期値（initialValue）は0とする。

```js
const array = [0, 1, 2, 3]
const initialValue = 0

const result = array.reduce(
  (accumulator, currentValue, currentIndex, array) => {
    console.log(
      "accumulator:",
      accumulator,
      "currentValue:",
      currentValue,
      "currentIndex:",
      currentIndex,
      "array:",
      array
    )
    return accumulator + currentValue
  },
  initialValue
)

console.log("result:", result) //6
```
<img width="517" alt="スクリーンショット 2020-07-11 19.53.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218656/dd8fd811-0ced-f95d-3edf-17d1e56783ab.png">

reduce() の返値は、最後の返値である (6) となった。

##### これをforで書くと。。。

```js
const array = [0, 1, 2, 3]
let value = 0

for (let i = 0; i < array.length; i++) {
  value += array[i]
}

console.log(value) //6
```

##### これをforEach()で書くと。。。

```js
const array = [0, 1, 2, 3]
const forEachResult = array => {
  let sum = 0
  array.forEach(number => {
    return (sum += number)
  })
  return sum
}

console.log("forEachResult:", forEachResult(array)) //6
```

forやforEach()は、「配列の要素を1つずつ処理する」というのが本来の目的であり、reduce() は「単一の値を返す」のが目的なので、reduce()を使うほうが、今回の目的「arrayの数字を足し上げて、合計値を得る」手段としては、適していると言える。

### 理解を深めるために色々なサンプルコードを書いてみる

#### sample01: オブジェクトの配列の値の合計値を出す

```js
const initialValue = 0
const array = [{ x: 0 }, { x: 1 }, { x: 2 }, { x: 3 }]

const sample01 = array.reduce((accumulator, currentValue) => {
  return accumulator + currentValue.x
}, initialValue)

console.log(sample01) //6
```

#### sample02: 配列から要素を抜き出して、好きな形のオブジェクトにする

```js
const initialValue = {}
const array = [
  { name: "keko", age: 25 },
  { name: "taro", age: 19 },
  { name: "mai", age: 17 }
]

const sample02 = array.reduce((accumulator, currentValue) => {
  accumulator[currentValue.name] = currentValue.age
  return accumulator
}, initialValue)

console.log(sample02) //{keko: 25, taro: 19, mai: 17}

```

#### sample03: 配列をindex付きのオブジェクトにする

```js
const initialValue = {}
const array = [
  { name: "keko", age: 25 },
  { name: "taro", age: 19 },
  { name: "mai", age: 17 }
]

const sample03 = array.reduce((accumulator, currentValue, index) => {
  accumulator[index + 1] = currentValue
  return accumulator
}, initialValue)

console.log(sample03)
// {
//   1: {name: "keko", age: 25}
//   2: {name: "taro", age: 19}
//   3: {name: "mai", age: 17}
// }
```

#### sample04: 配列を欲しい要素だけにしてオブジェクトにする

```jsx
const initialValue = []
const array = [
  { name: "keko", age: 25, pref: "Ehime" },
  { name: "taro", age: 19, pref: "Ehime" },
  { name: "mai", age: 17, pref: "Osaka" }
]

const sample04 = {
  ...array.reduce((accumulator, currentValue) => {
    const { name, age } = currentValue
    accumulator.push({ name, age })
    return accumulator
  }, initialValue)
}

console.log(sample04)
// {
//   { name: "keko", age: 25 },
//   { name: "taro", age: 19 },
//   { name: "mai", age: 17 }
// }
```

#### sample05: プロパティによってオブジェクトをグループ化する

```js
const initialValue = {};
const array = [
  { name: "keko", age: 25, pref: "Ehime" },
  { name: "taro", age: 19, pref: "Ehime" },
  { name: "mai", age: 17, pref: "Osaka" }
];

const sample05 = array.reduce((accumulator, currentValue) => {
  let key = currentValue.pref;
  if (!accumulator[key]) {
    accumulator[key] = [];
  }
  const { name, age } = currentValue;
  accumulator[key].push({ name, age });
  return accumulator;
}, initialValue);

console.log(sample05);

// {
//   Ehime: [
//     {name: "keko", age: 25 },
//     {name: "taro", age: 19 }],
//   Osaka: [
//     {name: "mai", age: 17 }]
// }
```

#### sample06_1: 二次元配列を一次元配列にする(concat編)

```js

const initialValue = []
const array = [[0, 1], [2, 3], [4, 5]]

const sample06_1 = array.reduce((accumulator, currentValue) => {
  return accumulator.concat(currentValue)
}, initialValue)

console.log(sample06_1) // [0, 1, 2, 3, 4, 5]
```

#### sample06_2: 二次元配列を一次元配列にする(spred演算子編)

```js
const initialValue = []
const array = [[0, 1], [2, 3], [4, 5]]

const sample06_2 = array.reduce((accumulator, currentValue) => {
  return [...accumulator, ...currentValue]
}, initialValue)

console.log(sample06_2) // [0, 1, 2, 3, 4, 5]
```

#### sample07_01: 配列内の重複している値を除外する(someで除外編)

```js
const initialValue = []
const array = [1, 2, 2, 3, 1, "a", "b", "b"]

const sample07_01 = array.reduce((accumulator, currentValue) => {
  if (!accumulator.some(item => item === currentValue)) {
    accumulator.push(currentValue)
  }
  return accumulator
}, initialValue)

console.log(sample07_01)
```

#### sample07_02: 配列内の重複している値を除外する(indexOfで除外編)

```js
const initialValue = []
const array = [1, 2, 2, 3, 1, "a", "b", "b"]

const sample07_02 = array.reduce((accumulator, currentValue) => {
  if (accumulator.indexOf(currentValue) === -1) {
    return accumulator.push(currentValue)
  }
  return accumulator
}, initialValue)

console.log(sample07_02)
```

#### sample08: filter() + map() の代わりに使う

map() は入力＆出力は要素数は変わらないので、配列の一部要素を除外する時、filter() を適用させる必要があり、filter() と map() でそれぞれループするが、reduce() を使えばループは一度で済む。

```js
const initialValue = []
const array = [
  { id: 1, name: "keko", age: 25 },
  { id: 2, name: "taro", age: 19 },
  { id: 3, name: "mai", age: 17 },
  { id: 4, name: "hiro", age: 30 }
]

//未成年を除外して、idとnameを取得して配列にする
const sample08 = array.reduce((accumulator, currentValue) => {
  if (currentValue.age > 20) {
    const { id, name } = currentValue
    accumulator.push({ id, name })
  }
  return accumulator
}, initialValue)

console.log(sample08)
// [
//   {id: 1, name: "keko"},
//   {id: 4, name: "hiro"}
// ]


//filter().map()の場合
const sample08_test = array
.filter(item => item.age > 20)
.map(item => {
  const { id, name } = item;
  return { id, name }
})

console.log(sample08_test)
// [
//   {id: 1, name: "keko"},
//   {id: 4, name: "hiro"}
// ]
```

#### sample09: 多次元配列を一次元配列にする

```js
const array = [0, [1, 2, 3, [4, 5], [6, 7, [8, 9]]]];

const mergeArry = arr => {
  const result = arr.reduce((acc, current) => {
    return acc.concat(current);
  }, []);
  return result;
};

const hasArry = arr => arr.some(item => Array.isArray(item));

const mergeAll = arr => {
  if (!hasArry(arr)) {
    return arr;
  }
  const result = mergeArry(arr);
  return mergeAll(result);
};

const sample09 = mergeAll(array);

console.log(sample09); //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```

今後もsample追加していく

### 参考

- MDN: [Array.prototype.reduce()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
- KDE BLOG:[【JavaScript基礎】Array.prototype.reduce() をしっかり理解する＆サンプル集](https://kde.hateblo.jp/entry/2018/10/13/065738)