JavaScript約8分

mapで表示用の文字列を作る:元データを残して一覧の文言をそろえる

一覧の名前へ所要時間や受付状態を付けたいときに。架空の体験講座データからmapで表示用の文字列を作り、全件に同じ規則を適用します。return漏れと元データの書き換えを、実行結果から確かめる練習です。

この記事の目次
  1. 完成する三つの文言を、コードより先に書く
  2. 一件の変換を関数にし、mapから呼び出す
  3. 結果がundefinedなら、画面への出力よりreturnを見る
  4. 元の名前まで変わったら、コールバック内の代入を外す
  5. 件数を減らす処理と、表示形式を変える処理を分ける
  6. 二件目のデータだけを変え、他の文言が残るか

講座一覧へ「名前・所要時間・受付状態」を表示するとき、手で文章を一件ずつ作ると表記がずれます。元の名前そのものへ説明を付け足すと、別の場所で名前だけを使う際にも困ります。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までお願いします。

← 記事一覧に戻る