forEach で配列をまわす
このレッスンで分かること
配列.forEach((要素) => { 処理 })で配列を 1 つずつ処理できます- コールバックは第 2 引数でインデックスも受け取れます
forEachは途中でbreakできません。中断が要るならfor-ofを使います
forEach で配列をまわす とは
配列の各要素に対してコールバック関数を順番に呼んでくれるメソッドです。作品一覧の組み立てを
forEachで書き直します。
ループとコールバックが合わさる
第5章で for と for-of を学び、前のレッスンでコールバックを学びました。この 2 つが合わさったものが forEach です。配列が自分で自分をまわしてくれて、要素ごとに渡した関数を呼んでくれます。
まずは for-of と並べてみましょう。山田 太郎さんの作品一覧を表示するコードです。
const works = ["プロフィールサイト", "自己紹介カード", "スキル一覧の表"];
// for-of
for (const work of works) {
console.log(work);
}
// forEach
works.forEach((work) => {
console.log(work);
});結果はまったく同じです。forEach は配列が持っているメソッドで、引数にコールバック関数を 1 つ受け取ります。配列の要素を先頭から順に取り出し、その要素を引数にしてコールバックを呼びます。ループの仕組みは forEach の側にあるので、こちらは「各要素に何をするか」だけを書きます。
forEachに渡しているのはアロー関数のコールバックです。前のレッスンで学んだfnを渡す形が、そのままここで実用されています。
インデックスも受け取れる
コールバックは引数を最大 3 つ受け取れます。よく使うのは最初の 2 つです。
| 引数 | 中身 |
|---|---|
| 第 1 引数 | 要素そのもの |
| 第 2 引数 | インデックス。0 から始まる |
| 第 3 引数 | 配列そのもの |
番号付きの一覧を作ってみましょう。
const works = ["プロフィールサイト", "自己紹介カード", "スキル一覧の表"];
const lines = [];
works.forEach((work, index) => {
lines.push((index + 1) + ". " + work);
});
console.log(lines.join("\n"));
// 1. プロフィールサイト
// 2. 自己紹介カード
// 3. スキル一覧の表必要な引数だけ書けばよく、インデックスが不要なら第 1 引数だけで構いません。第 3 引数の配列そのものは、入門段階ではほとんど使いません。
戻り値は undefined
forEach 自体の戻り値は常に undefined です。結果を集めたいときは、外側に配列を用意して push するか、return で値を集める別のメソッドを使います。
const works = ["プロフィールサイト", "自己紹介カード"];
const result = works.forEach((work) => work + " 完成");
console.log(result); // undefinedforEach は「各要素に何かする」ための道具であって、「各要素を変換した新しい配列を作る」道具ではありません。変換したいときに使う map、絞り込みたいときに使う filter、1 つの値にまとめたいときに使う reduce という仲間がいます。この 3 つは実践 JavaScript のコースで扱うので、いまは名前だけ覚えておいてください。
空の配列に
forEachを呼んでもエラーにはならず、コールバックが 1 度も呼ばれずに終わります。要素数 0 を特別扱いするifは要りません。
for-of との使い分け
どちらも配列を 1 つずつまわす道具ですが、性格が違います。
| 観点 | for-of | forEach |
|---|---|---|
| 書き方 | 構文 | 配列のメソッド |
| 途中で中断 | break できる | できない |
| 次の要素へ飛ばす | continue できる | return で代用 |
| インデックス | entries() などが要る | 第 2 引数で取れる |
| 戻り値 | 無し | undefined |
| 相性 | 中断・スキップがある処理 | 全要素に同じ処理 |
いちばん大事なのが中断できるかどうかです。forEach の中で break を書くと構文エラーになります。
// NG SyntaxError になる
works.forEach((work) => {
if (work === "自己紹介カード") break;
console.log(work);
});forEach に渡しているのは関数なので、その中の break はループを抜ける文として成立しません。ループを止めているのは forEach の側であり、コールバックからは手が届かないのです。
途中で止めたいなら for-of を使います。
for (const work of works) {
if (work === "自己紹介カード") break;
console.log(work);
}一方、その要素だけ飛ばしたいだけなら forEach でも return で代用できます。コールバックが終わるだけで、次の要素の処理は続きます。
works.forEach((work) => {
if (work === "自己紹介カード") return; // この要素だけ飛ばす
console.log(work);
});この return は continue に相当します。break の代わりにはならない、という点を取り違えないでください。
見つかった時点で探索を打ち切りたい場面は実務でよくあります。そういうときは
for-ofか、第7章で学ぶfindを使います。全件に同じ処理をするならforEachが読みやすい、という住み分けです。
定番の型 lines に集めて join する
forEach で表示用の文字列を作るときは、次の 3 手順が定番になります。
- 空の配列を用意する
forEachの中でpushして行を足していく- 最後に
joinで 1 本の文字列にする
function listWorks(works) {
const lines = [];
works.forEach((work, index) => {
lines.push((index + 1) + ". " + work);
});
return lines.join("\n");
}
console.log(listWorks(["プロフィールサイト", "自己紹介カード"]));
// 1. プロフィールサイト
// 2. 自己紹介カードlines は関数の中で宣言します。前のレッスンで学んだとおり、関数の外に置くと 2 回目に呼んだときに前回の行が残ります。
条件で行を飛ばしたいときは、push の前に return を書きます。連番を飛ばしたくないなら、index ではなく lines.length + 1 を番号に使うと、実際に追加した行数どおりの番号になります。
works.forEach((work) => {
if (work === "") return;
lines.push((lines.length + 1) + ". " + work);
});よくある間違い
1. forEach の戻り値を使おうとする
// NG 戻り値は undefined
const titles = works.forEach((work) => work + " 完成");
// OK 外の配列に集める
const titlesOk = [];
works.forEach((work) => {
titlesOk.push(work + " 完成");
});2. break で抜けようとする
// NG 構文エラー
works.forEach((work) => {
if (work === "自己紹介カード") break;
});中断が必要になった時点で for-of に書き換えます。無理に forEach のまま解決しようとしないでください。
3. コールバックに丸括弧を付けて渡す
const show = (work) => console.log(work);
// NG その場で 1 回だけ実行され、undefined が渡される
works.forEach(show());
// OK 関数そのものを渡す
works.forEach(show);前のレッスンの fn と fn() の区別が、そのままここでも効いてきます。
要件
- 配列の走査には forEach を使うこと
- 行を集める配列は関数の中で宣言すること
- 空文字列の要素は飛ばし、連番は実際に追加した行数どおりに振ること
- 表示できる作品が 0 件のときは文字列
作品はまだありませんを返すこと
入出力例
listWorks(["プロフィールサイト","自己紹介カード","スキル一覧の表"]) → "1. プロフィールサイト
2. 自己紹介カード
3. スキル一覧の表"
listWorks(["プロフィールサイト","","スキル一覧の表"]) → "1. プロフィールサイト
2. スキル一覧の表"
listWorks(["自己紹介カード"]) → "1. 自己紹介カード"
listWorks([]) → "作品はまだありません"
listWorks(["",""]) → "作品はまだありません"