JavaScriptにおける繰り返し構文、反復処理まとめ

## はじめに

自分自身が繰り返し構文、反復処理メソッドをなんとなくで使っている気がして、一度一通り勉強しなおそうということで記事にしました。

## 繰り返し構文、反復処理メソッド一覧

- 繰り返し構文
  - for
  - for...in
  - for...of
  - do while
  - while
- 反復処理メソッド
  - forEach
  - map
  - filter
  - reduce

よく使われる、よく聞く繰り返し処理を列挙しました。

## for

```javascript
const array = [1, 2, 3];
for (let i = 0; i < array.length; i++) {
  console.log(array[i]);
}
// => 1
// => 2
// => 3
```

スタンダードな繰り返し文。

上記のコードでは、変数`i`が実行回数を保持し、`for`文の中の処理が終わったら変数`i`に 1 ずつ足して実行回数をカウントする。変数`i`が配列の長さ分より数値が大きくなったら処理終了。

繰り返し条件の変更もしやすく汎用性が高い半面、パッと見で何の処理を行う繰り返し文なのかが分かりづらい特徴がある。

## for...of

```javascript
const array = ["foo", "bar", "baz"];
for (const i of array) {
  console.log(i);
}
// => foo
// => bar
// => baz
```

基本は配列に有効な繰り返し文。

オブジェクトのプロパティーのキーだけ出力したい、プロパティーの値だけ出力したいといった場合には、下記のようにオブジェクトを配列に変換する処理をはさむ必要がある。

### 例.プロパティーのキーを出力

```javascript
const array = { a: 1, b: 2, c: 3 };
const newArray = Object.keys(array); // newArray => ["a", "b", "c"]
for (const key of newArray) {
  console.log(key);
}
// => a
// => b
// => c
```

### 例.値を出力

```javascript
const array = { a: 1, b: 2, c: 3 };
const newArray = Object.values(array); // newArray => ["1", "2", "3"]
for (const value of newArray) {
  console.log(value);
}
// => 1
// => 2
// => 3
```

### 例.プロパティーのキーと値をセットにして出力

```javascript
const array = { a: 1, b: 2, c: 3 };
const newArray = Object.entries(array); //newArray => [["a", 1], ["b", 2], ["c", 3]]
for (const entry of newArray) {
  console.log(entry);
}
// => ["a", 1]
// => ["b", 2]
// => ["c", 3]
```

## for...in

オブジェクト・配列に対して使える繰り返し文。

`for`文と同じように処理条件内で変数を指定して、それをプロパティーのキーにしてプロパティーの値を出力する場合に使いやすい。

実行環境などによって必ずしも順序通りの出力に関して保証されないため、順序を気にせず列挙する場合に推奨。
※順序通りの出力を担保してほしい場合は`for`文が推奨されている。

```javascript
const array = { a: 1, b: 2, c: 3 };
for (const i in array) {
  console.log(`${i}:${array[i]}`);
}
// => a: 1
// => b: 2
// => c: 3
```

```javascript
const array = ["foo", "bar", "baz"];
for (const i in array) {
  console.log(i);
}
// => 0
// => 1
// => 2
```

### for...in と for...of の違い

処理対象が配列の場合、`for...in`は**プロパティーのキーに対して**繰り返し処理を行うが、`for...of`は**プロパティーの値に対して**処理を行うため適材適所。

処理対象がオブジェクトの場合、`for...in`は出力順序に確実性は保証されないがプロパティーのキーもプロパティーの値もアクセスしやすくシンプルで使いやすい。

`for...of`は基本オブジェクトに対して使用できないためプロパティーのキーのみもしくはプロパティーの値のみ、もしくはプロパティーのキーと値をセットにした配列のそれぞれを配列に変換してから処理を行う必要がある。

## forEach

繰り返し構文ではないが、配列に対して有効な反復処理メソッド。

配列の値を順繰りコールバック関数が一つ一つ処理する、シンプルで使いやすいメソッド。

```javascript
const array = ["foo", "bar", "baz"];
array.forEach((v) => {
  console.log(v);
});
```

### forEach と for...of の違い

`forEach`では`break`や`continue`が使えないため、`return`などで中断・スキップ処理を別の書き方をする必要があり、`break`や`continue`と違って一目で中断なのかスキップなのかが分かりづらい。

`for...of`でインデックスを取得するには`Object.entries`で変換する必要があるが、`forEach`では第二引数で`index`を指定することができるのでインデックスを扱いやすい。
基本的には扱いやすい`forEach`を使うのがよさそう。

```javascript
const array = [1, 2, 3];
for (const [index, v] of Object.entries(array)) {
  console.log(`index: ${index}, value: ${v}`);
}
// => index: 0, value: 1
// => index: 1, value: 2
// => index: 2, value: 3
```

### for...of で特定の巡回をスキップする

```javascript
const array = [1, 2, 3, 4, 5];
for (const [index, v] of Object.entries(array)) {
  if (index === "3") {
    continue;
  }
  console.log(`index: ${index}, value: ${v}`);
}
// => index: 0, value: 1
// => index: 1, value: 2
// => index: 2, value: 3
// => index: 4, value: 5
```

### forEach で特定の巡回をスキップする

```javascript
const array = [1, 2, 3, 4, 5];
array.forEach((v, index) => {
  if (index === 2) {
    return;
  }
  console.log(v);
});
// => 1
// => 2
// => 4
// => 5
```

`forEach`メソッドは第二引数に`index`を指定することができ、上記では 3 回目で`return`を指定することでスキップしている。

## do...while

指定した条件に対して`false`になるまで実行を続ける繰り返し構文。

条件は処理を行った後に判定されるため、1 巡目で`false`となる条件でも 1 回実行されてからループを終了する。

```javascript
let count = 0;

do {
  count++;
  console.log(count);
} while (count < 5);
// => 1
// => 2
// => 3
// => 4
// => 5
```

```javascript
let count = 10;

do {
  count++;
  console.log(count);
} while (count < 5);
// => 11 初期値で条件を満たしていても1回実行される
```

## while

`do...while`と処理は似ているが、条件の判定が行われてから処理が走る。

```javascript
let count = 0;

while (count < 5) {
  count++;
  console.log(count);
}
// => 1
// => 2
// => 3
// => 4
// => 5
```

```javascript
let count = 10;

while (count < 5) {
  count++;
  console.log(count);
}
// 条件がすでにfalseなためログに出力されない
```

### while と for の違い

どちらも似た繰り返し構文だが、`for`は**繰り返しの回数が決まっている**場合、`while`は**繰り返し回数が決まっておらず条件が`false`にならない限り処理を続けたい**場合で使い分けるのが慣例的。

## map

繰り返し構文ではなく反復処理メソッドで、配列の要素一つ一つを順番にコールバック関数の処理を行い、新たな配列を生成する。

```javascript
const array = [1, 2, 3, 4, 5];
const mapArray = array.map((v) => v * 2);
console.log(mapArray);
// => [2, 4, 6, 8, 10]
```

## filter

こちらも繰り返し構文ではないが反復処理メソッドで、コールバック関数が`true`を返す要素だけを抽出して新たな配列を生成する。

```javascript
const array = [1, 2, 3];
const newArray = array.filter((v) => v % 2 === 1);
console.log(newArray);
// => [1, 3]
```

## reduce

コールバック関数で初期値に対して順番に要素を足し合わせて（積み重ねて）、配列から配列以外の累積値を生成する反復処理メソッド。

```javascript
const array = [1, 2, 3];
const newArray = array.reduce((accu, curr) => accu + curr);
console.log(newArray);
// => 6 初期値であるaccuには0が入り、0 + 1 + 2 + 3 = 6となる
```

### オブジェクトに対する forEach、map、filter、reduce

上記の反復処理メソッドは基本的に配列に対する処理を得意としている。

オブジェクトに対して使用するためには`Object.values`や`Object.entries`などを使用して配列に変換する前段階が必要。

## まとめ

| 構文     | 主な対象           | 使用場面                                                                                       |
| -------- | ------------------ | ---------------------------------------------------------------------------------------------- |
| for      | なんでも OK        | 繰り返し回数が決まっている処理                                                                 |
| for...in | 配列・オブジェクト | 配列ならばキーに対して処理を行いたい場合に<br>オブジェクトならば出力順序の正確さを求めない場合 |
| for...of | 配列               | プロパティーの値に対して処理を行いたい場合                                               |
| while    | なんでも OK        | 条件が`false`にならない限り繰り返したい処理                                                    |
| do while | なんでも OK        | 条件が`false`にならない限り繰り返したい処理<br>かつ 1 回は処理を行いたい場合                   |
| forEach  | 配列               | 配列からコールバック関数で汎用的な処理をしたい場合）                                           |
| map      | 配列               | 配列からコールバック関数による処理で新たな配列を生成したい場合・                               |
| filter   | 配列               | 配列からコールバック関数による処理で`true`を返す要素を抽出した新たな配列を生成したい場合・     |
| reduce   | 配列               | 配列からコールバック関数による処理で配列から配列以外の要素の累積値を生成したい場合・           |