JavaScript で配列からランダムに抽出するコードをいま一度振り返るメモ

JavaScript で配列からランダムに抽出するコードをいま一度振り返るメモです。

経緯

JavaScript で配列からランダムに抽出するコードって、Math.random で浮動小数点の擬似乱数をもらいつつ、配列の長さ(個数)を掛けて、Math.floor で切り捨てれば動かせると、なかば暗記ぎみに書いてました。

そして、この、なかば暗記ぎみのものが教えるときに不安になってしまって、即席で調整をかけてしまったら、ものの見事に、解釈を間違って、レクチャーの現場で間違えてしまいました!

もちろん、その場で参加者と一緒に、従来通りの正しいコードに直しましたが、おさらいしておきます。

正しいコード

以前から暗記していたものですね。結果として、正しかったです。

今回は、春夏秋冬の配列でランダムで一つ抽出するものです。

// 春夏秋冬の配列
const seasons = [
  "春",
  "夏",
  "秋",
  "冬"
];

// 配列からランダムに選ぶためのインデックス
const randomIndex = Math.floor(Math.random() * 4);

// ランダムに選ばれた seasons
const currentSeason = seasons[randomIndex];

console.log(currentSeason);

Math.floor(Math.random() * 4) の 4 の部分は配列の長さを seasons.length で割り当てることが多いですが、今回は挙動を分かりやすくするために 4 の整数で固定値にしています。

実行してみると、もちろん、春夏秋冬がランダムで返ってきます。

間違ったコード

こちらが間違ったコードです。これだと、春夏秋しかランダムで返ってきません。冬は出てこないです。

// 春夏秋冬の配列
const seasons = [
  "春",
  "夏",
  "秋",
  "冬"
];

// 配列からランダムに選ぶためのインデックス
// ここを変更した
const randomIndex = Math.floor(Math.random() * 3);

// ランダムに選ばれた seasons
const currentSeason = seasons[randomIndex];

console.log(currentSeason);

どこを変更したかというと、配列からランダムに選ぶためのインデックスの処理で Math.random に掛ける値を 4 から 3 にしちゃってます。

今回疑ってしまったのは、Math.random

資料作成の佳境で、とっさに疑うとよくないですね。

Math.random が返す浮動小数点の擬似乱数を 0.0 から 1.0 ぴったりと考えてしまって、4 を掛ける形だと 0 , 1 , 2 , 3 , 4 が来てしまうと思ってしまったんです。

この予想だと、0 , 1 , 2 , 3 まではいいんですが、4が来てしまうと seasons にインデックス 4 つまり 5 番目の配列がないのでエラーになると勘違いしてしまったのです。

なので、即席で掛ける値を 3 にして 0 , 1 , 2 , 3 がくるかな?と対応してしまったんですが、予想した挙動ではなく実行すると 0 , 1 , 2 つまり春夏秋しかランダムで返ってきませんでした。

Math.random を改めて確認

floor, random メソッドを確認してみます。

とのことで、floor が日本語で言うところの切り捨てということは合っていまして、間違っていたのは random ということがわかりました。

0 以上 1 未満 (0 は含むが、 1 は含まない) の範囲

なんですね。つまり、0.0 から 1.0 ぴったりと「1 を含む」と考えてしまったところが勘違いでした。

1 は含まないので、Math.floor(Math.random() * 4); で、0 , 1 , 2 , 3 を返すことができるので、なかば暗記していたほうの従来のコードで合っていたわけです。

よく見ると、Math.random() – JavaScript | MDN のサンプルコードでも floor , random の組み合わせでランダムつくってますね。

ということで、いま一度振り返ってみまして、より詳細に挙動の理解ができました。