MDN Web DocsのJavaScript課題「バカ話ジェネレーター」に取り組んだ

こんにちはWebエンジニアのmasakichiです。

## これは何の記事
MDN Web DocsにあるJavaScript課題「バカ話ジェネレーター」に取り組んでみました。
コードの中身は[GitHub](https://github.com/Naughty1029/Silly_story_generator)からもご確認いただけます。

## なんで記事にしたの？
これからJavaScriptの学習を進めていく方の参考になればなというのがひとつです。
また、わたし自身もまだまだ学習中の身ですので、もっとこうした方がいいよ！などのご指摘があればコメント欄などでご教示頂けたら嬉しいなと思ったからです。

## バカ話ジェネレーターって？
MDNがJavaScriptのチュートリアルとして提供している課題のひとつです。
詳しい内容は[こちら](https://developer.mozilla.org/ja/docs/Learn/JavaScript/First_steps/Silly_story_generator)でご確認ください。

## それではわたしのコードです

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width">

    <title>Silly story generator</title>

    <style>

    body {
      font-family: helvetica, sans-serif;
      width: 350px;
      margin: 0 auto;
    }

    label {
      font-weight: bold;  
    }

    div {
      padding-bottom: 20px;
    }

    input[type="text"] {
      padding: 5px;
      width: 150px;
    }

    p {
      background: #FFC125;
      color: #5E2612;
      padding: 10px;
      visibility: hidden;
    }

    </style>
  </head>

  <body>
    <div>
      <label for="customname">Enter custom name:</label>
      <input id="customname" type="text" placeholder="">
    </div>
    <div>
      <label for="us">US</label><input id="us" type="radio" name="ukus" value="us" checked>
      <label for="uk">UK</label><input id="uk" type="radio" name="ukus" value="uk">
    </div>
    <div>
      <button class="randomize">Generate random story</button>
    </div>
    <p class="story"></p>
    <script src="./main.js"></script>
  </body>
</html>
```

```javascript:main.js
"use strict";

const customName = document.getElementById('customname');
const randomize = document.querySelector('.randomize');
const story = document.querySelector('.story');
let storyText = "It was 94 fahrenheit outside, so :insertx: went for a walk. When they got to :inserty:, they stared in horror for a few moments, then :insertz:. Bob saw the whole thing, but was not surprised — :insertx: weighs 300 pounds, and it was a hot day.";
//keyをplaceholderの正規表現として使いながらループを回したいので連想配列をつくる。
let insertItem = new Map();
insertItem.set("insertX", ["Willy the Goblin","Big Daddy","Father Christmas"]);
insertItem.set("insertY", ["the soup kitchen","Disneyland","the White House"]);
insertItem.set("insertZ", ["spontaneously combusted","melted into a puddle on the sidewalk","turned into a slug and crawled away"]);

randomize.addEventListener('click', result);

function result() {
    let newStory = storyText;
    for (const [key, value] of insertItem) {
        //配列からランダムに1つの文字列を抽出
        let Item  = randomValueFromArray(value);
        //placeholderの置換
        newStory = replacePlaceholder(newStory,Item,key);
    }

    if(customName.value !== '') {
        const name = customName.value;
        newStory = newStory.replace("Bob",name);
    }

    if(document.getElementById("uk").checked) {
        //const weight = 300;
        //const temperature = 94;
        //重さと温度の単位をポンド・華氏から、ストーン・摂氏に変換
        //変換式はここで検索 https://www.metric-conversions.org/ja/
        //const Stone = Math.round(weight * 0.071429)+' stone';
        //const Celsius =  Math.round((temperature-32)/1.8)+' centigrade';
        //newStory = newStory.replace("300 pounds" , Stone);
        //newStory = newStory.replace("94 fahrenheit" , Celsius);

        //修正 @fq79074さんより
        newStory = newStory
        .replace(/(\d+)\s*pounds/ig, (a, b) => `${Math.round(b * 0.071429)} stone`)
        .replace(/(\d+)\s*fahrenheit/ig, (a, b) => `${Math.round((b - 32) / 1.8)} centigrade`);
    }

    story.textContent = newStory;
    story.style.visibility = 'visible';
}

function randomValueFromArray(array){
    const random = Math.floor(Math.random()*array.length);
    return array[random];
}

function replacePlaceholder(newStory,item,placeholder){
    const regex = new RegExp(`:${placeholder}:`, 'ig');
    return newStory.replace(regex, item);
}
```

工夫した点として、プレースホルダーに代入する配列をMap関数で連想配列にしたところです。
replacePlaceholder関数のところでは連想配列のkeyを参照して、正規表現で変数storyTextの文字列を置換しています。
正規表現でgのフラグを用いると文字列で該当する全てのマッチ箇所を置換してくれるようになります。

## 修正（main.js) 2022.1.5
@fq79074 さんから[コメント欄](https://qiita.com/Naughty1029/items/7f4df1bba77a800270e0#comment-6481805930bd2af2138d)にてご指摘いただき、上記main.jsのコードを一部修正しました。
replace関数に正規表現とigオプション。コールバック関数を用いて重さと温度の単位変換部分を修正しました。
とても勉強になるご指摘ありがとうございました！

## 参考リンク先
課題に取り組む上で下記サイトを参考にさせて頂きました。感謝！

- [MDN String.prototype.replace()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/replace)
- [MDN バカ話ジェネレーター](https://developer.mozilla.org/ja/docs/Learn/JavaScript/First_steps/Silly_story_generator)
- [正規表現を使ったマッチングに変数を使用する](https://qiita.com/ykob/items/6e4d0b07bed57881a2bd)

## ライセンス
[Silly_story_generator](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Silly_story_generator) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Silly_story_generator/contributors.txt) is licensed under CC-BY-SA 2.5.