この記事の目次
講座一覧へ「名前・所要時間・受付状態」を表示するとき、手で文章を一件ずつ作ると表記がずれます。元の名前そのものへ説明を付け足すと、別の場所で名前だけを使う際にも困ります。mapで表示用の文字列を作る処理を用意し、情報を持つ配列と、読者へ見せる文言を分けましょう。
今回は架空の地域センターの体験講座を扱います。配列、オブジェクト、関数を少し読める人向けの練習です。ブラウザーの開発者ツールのコンソール、またはNode.jsを使います。HTMLへの描画や申し込み機能は含めず、文字列の配列が完成したところで確認します。
完成する三つの文言を、コードより先に書く
入力データの一件にはname、minutes、openの三項目を持たせます。nameは空でない文字列、minutesは正の整数、openはtrueかfalseという前提です。外部から取得した未検証の値や、途中の要素が抜けた配列は、この例へ渡しません。
完成形は「紙の箱づくり(30分)/受付中」「糸のしおり(45分)/受付終了」「布のスタンプ(60分)/受付中」の三つです。どれも同じ位置に括弧、単位、区切り、状態が入ります。まず一件分の規則を決めると、三件目だけ単位を付け忘れるような差を見つけられます。
ここで必要なのは、受付中の講座を選び出すことではありません。受付終了も含む三件すべてについて、対応する一つの文言が必要です。入力の一件から出力の一件へ変換する処理として、mapを選びます。
一件の変換を関数にし、mapから呼び出す
mapは配列の各要素に対して関数を呼び、その戻り値を集めた新しい配列を返します。そのように別の処理へ渡す関数を「コールバック」と呼びます。MDNのmapの説明でも、結果の配列に入るのは関数の戻り値だと確認できます。
次を一まとまりで実行してください。Node.jsならlabels.jsとして保存し、同じフォルダでnode labels.jsを実行します。ブラウザーなら、自分で用意した練習ページのコンソールで動かせます。
const workshops = [
{ name: "紙の箱づくり", minutes: 30, open: true },
{ name: "糸のしおり", minutes: 45, open: false },
{ name: "布のスタンプ", minutes: 60, open: true }
];
function makeLabel(workshop) {
const status = workshop.open ? "受付中" : "受付終了";
return `${workshop.name}(${workshop.minutes}分)/${status}`;
}
const labels = workshops.map(makeLabel);
console.log(labels);
console.log(workshops[0].name);
最初の出力は先ほど書いた三つの文字列を持つ配列、次の出力は「紙の箱づくり」です。makeLabelは一件の情報から文言を返すだけで、元のnameへ代入していません。表示のために追加した「30分」などはlabels側にだけ残ります。
処理を一件目で追うと、workshopへ先頭のオブジェクトが入り、openがtrueなのでstatusは「受付中」になり、returnの行で一つの文字列を返します。mapがその値を結果の先頭へ入れ、次の要素にも同じ処理を行います。テンプレートリテラルの${...}には、各項目の値を埋め込んでいます。
makeLabelを別の関数にしたのは、一件だけ渡して確認できるようにするためです。結果がおかしいときはmakeLabel(workshops[1])を実行し、二件目の文言が正しいかを先に調べられます。全体の配列と一件の変換を行き来して確認しましょう。
結果がundefinedなら、画面への出力よりreturnを見る
次は意図的な失敗例です。最初のコードを実行した後に続けて試せます。
const missingLabels = workshops.map((workshop) => {
makeLabel(workshop);
});
console.log(missingLabels); // [undefined, undefined, undefined]
関数を呼んで文言は作っていますが、外側のアロー関数から何も返していません。アロー関数は=>を使う関数の書き方で、このように波括弧で本体を囲む場合はreturnが必要です。MDNのアロー関数の説明で、式だけの本体との違いを確認できます。
修正は、中の行をreturn makeLabel(workshop);へ替えることです。または最初の完成例のようにmakeLabel自体をmapへ渡せます。修正後は「エラーが消えたか」だけでなく、配列の三要素がすべて文字列になり、期待する文言と一致するか確認します。
console.logで文言を出すだけでも、mapの結果へその文言を渡したことにはなりません。途中の出力が正しいのに最終結果がおかしい場合は、「何を表示したか」と「何を返したか」を別々に読みます。
元の名前まで変わったら、コールバック内の代入を外す
もう一つの失敗は、関数の中でworkshop.name = makeLabel(workshop)のように代入してしまうことです。結果の配列が新しくても、受け取ったオブジェクトのnameを書き換えれば元データも変わります。mapという名前だけで、内部の変更まで防がれるわけではありません。
この場合、処理をもう一度行うと、既に「30分/受付中」を含む名前へ、同じ説明がさらに付くことがあります。再実行するほど文言が長くなるなら、入力を表示文で上書きしていないか調べます。
修正ではnameへの代入を除き、完成例のように文字列を返すだけにします。その後、元の三件のデータを初期状態へ戻して再実行してください。一度書き換えたデータを使ったままでは、修正した関数の結果を正しく比べられません。
再確認は、二回作ったlabelsの内容が同じで、workshopsのname・minutes・openが初期値のままかを見ます。別の表示が必要な場合も、元データを残しておけば、そこから名前だけの配列などを作り直せます。
件数を減らす処理と、表示形式を変える処理を分ける
受付中だけを載せたいなら、先に対象を選ぶ必要があります。その判断はfilterによる絞り込みの記事を参照できます。mapの中で受付終了の場合だけreturnを書かない方法では、その場所にundefinedが残り、欲しい一覧になりません。
元の順番を変える処理は配列の並べ替えの記事の範囲です。今回の完成条件は、登録順のまま全件を同じ形式へ変えること。絞り込みや並べ替えも必要になったら、各段階の配列を確認してから組み合わせます。
結果の配列が不要で、各要素について記録を出すだけなら、for...ofなどの繰り返しを選ぶ方法があります。反対に、この例のように変換後の値を次の処理へ渡すならmapが候補になります。できた文字列をHTMLへ表示する段階では、textContentの記事を使い、データ変換とは別に表示先を決めます。
二件目のデータだけを変え、他の文言が残るか
最初のデータへ戻してから、二件目のminutesを50、openをtrueに変えます。期待する二件目は「糸のしおり(50分)/受付中」です。makeLabelのコードはそのままで、この文言になるか確認してください。
- 元の三件を渡した場合、結果も三件で、名前の順序が一致する。
- 二件目のデータだけを変えた場合、一件目と三件目の文言は変わらない。
- 空配列に同じ変換を使う
[].map(makeLabel)の結果は空配列になる。 - 同じ入力から二度変換しても文言が重複せず、元のnameに所要時間が追記されない。
四つの期待結果を満たしたら、「一件の入力、返す文字列、結果の配列」の対応を一行で説明します。入力や画面操作と組み合わせる次の練習には、JavaScriptの無料レッスンを参照できます。まずは表示規則を変える場所がmakeLabelにまとまっていることを確かめてください。
記載内容へのご質問や訂正のご連絡は、hello@junkbranding.comまでお願いします。