map で形を変える
このレッスンで分かること
forEachは何も返さず、mapは変換した結果を集めた新しい配列を返しますmapは件数を減らせません。出てくる配列の長さは必ず元と同じです
入れ物を用意して push するのをやめる
入門では一覧を forEach で組み立てました。forEach は 1 件ずつ関数を呼ぶだけで、呼び出した側には何も返しません。だから結果をどこかに置いておくには、外側に空の配列を用意して push する必要がありました。
JavaScript
const skills = [
{ name: "CSS", years: 2 },
{ name: "JavaScript", years: 1 }
];
const lines = [];
skills.forEach((skill) => {
lines.push(skill.name + " (" + skill.years + ")");
});map を使うと、入れ物の用意と push が消えます。渡した関数が return した値が、そのまま新しい配列の同じ位置に入るからです。
JavaScript
const lines = skills.map((skill) => skill.name + " (" + skill.years + ")");
// ["CSS (2)", "JavaScript (1)"]行数が減っただけに見えますが、変わったのは意味のほうです。forEach は「何かをする」としか言っていません。map は「元の配列から新しい配列を作る」とはっきり言っています。読む人は中身を追わなくても、元と同じ件数の配列が出てくることを先に知ることができます。
forEach | map | |
|---|---|---|
| 戻り値 | undefined | 新しい配列 |
| 向いている仕事 | 画面に出すなど結果を残さない処理 | 表示用の形に変換して受け取る処理 |
forEach の中で return を書いても、その値はどこにも行きません。行くのは「その 1 件の処理を打ち切る」という意味だけです。値がほしいと思った時点で map に持ち替えます。
件数は必ず同じになる
map のいちばん大事な性質は、出てくる配列の長さが元と必ず同じになることです。5 件を渡せば 5 件返ります。ここで「公開されているものだけにしたい」と思っても、map では減らせません。
JavaScript
// これは意図どおりに動かない
const titles = works.map((work) => {
if (work.published) {
return work.title;
}
});
// 非公開の位置に undefined が残り、件数は減らない長さが必ず同じ、という性質には利点もあります。元の配列の 3 番目と、結果の 3 番目は必ず同じものを指しています。添字で対応が取れるので、別々に作った配列をあとから突き合わせられます。減らす処理を混ぜた瞬間にこの対応は崩れるので、map は形を変えるだけ、と役割を決めておくのが得だということです。
オブジェクトを返すときは丸かっこで包む
アロー関数を短い形で書くとき、そのまま波かっこを書くと関数の本体だと解釈されます。オブジェクトを返したいときは丸かっこで包みます。
JavaScript
// 波かっこが本体と見なされ、undefined が並ぶ
const bad = skills.map((skill) => { name: skill.name });
// 丸かっこで包むとオブジェクトが返る
const good = skills.map((skill) => ({ name: skill.name, years: skill.years }));この書き方は、表示に必要な項目だけを持つ軽い形を作るときによく使います。元のデータは画面で使わない項目まで持っていますが、出したいのはそのうち数個だけ、という切り分けができます。
元のオブジェクトに項目を足して返すのは避けます。
skill.text = ...と書くと元の配列の中身まで変わってしまい、あとで元データを見たときに何が本物か分からなくなります。
よくある間違い
- 戻り値を受け取り忘れる —
mapは元の配列を変えないので、変数に入れないと何も起きません。書きっぱなしにしても元の配列はそのままです - 波かっこ付きのアロー関数で
returnを書き忘れる — 短い形と長い形が混ざったときに起きます。undefinedが並んだら真っ先に疑ってください
要件
- 関数名は toWorkLabels とし、引数は作品オブジェクトの配列 works を 1 つだけ受け取る
- 戻り値は works と同じ件数の配列で、要素は title と label の 2 つのキーを持つオブジェクトにする
- label は year と views をつないだ文字列にし、2026年 / 120回表示 の形にそろえる
- 元の works は書き換えず、空配列を渡されたときは空配列を返す
入出力例
toWorkLabels([{"id":1,"published":true,"tags":["HTML","CSS"],"title":"プロフィールサイト","views":120,"year":2026},{"id":2,"published":true,"tags":["HTML"],"title":"自己紹介カード","views":48,"year":2026},{"id":3,"published":true,"tags":["HTML","CSS"],"title":"スキル一覧の表","views":32,"year":2025},{"id":4,"published":false,"tags":["JavaScript"],"title":"作品ギャラリー","views":8,"year":2025},{"id":5,"published":true,"tags":["JavaScript"],"title":"学習ログ","views":200,"year":2024}]) → [{"label":"2026年 / 120回表示","title":"プロフィールサイト"},{"label":"2026年 / 48回表示","title":"自己紹介カード"},{"label":"2025年 / 32回表示","title":"スキル一覧の表"},{"label":"2025年 / 8回表示","title":"作品ギャラリー"},{"label":"2024年 / 200回表示","title":"学習ログ"}]
toWorkLabels([{"id":3,"published":true,"tags":["HTML","CSS"],"title":"スキル一覧の表","views":32,"year":2025},{"id":5,"published":true,"tags":["JavaScript"],"title":"学習ログ","views":200,"year":2024}]) → [{"label":"2025年 / 32回表示","title":"スキル一覧の表"},{"label":"2024年 / 200回表示","title":"学習ログ"}]
toWorkLabels([{"id":9,"published":false,"tags":[],"title":"下書きの記事","views":0,"year":2026}]) → [{"label":"2026年 / 0回表示","title":"下書きの記事"}]
toWorkLabels([]) → []