JavaScriptで配列をシャッフルする

PHPには配列の要素をシャッフルする「shuffle」という関数がありますが、JavaScriptにはないので、メソッドを自作して使っています。
シャッフルというのは要素をランダムにするのではなく、要素の組合せは変えずに順番をランダムにすることです。
[0, 1, 2, 3, 4] を [3, 1, 2, 0, 4]にするような処理です。

ソートを使う方法

私が考えたのは次のような方法です。
配列の要素毎に値をランダムに与えます。
この値でソートすると元の配列がシャッフルされます。

Array.prototype.shuffle = function() {
  var array_length, keys, new_array, i;
  array_length = this.length;
  keys = [];
  new_array = [];
  for(i = 0; i < array_length; i++) {
    keys[i] = [this[i], Math.random()];
  }
  keys.sort(function(a, b) {
    return a[1] - b[1];
  });
  for(i = 0; i < array_length; i++) {
    new_array[i] = keys[i][0];
  }
  return new_array;
}

Fisher–Yatesの方法

上の方法でもできますが、どうにも回りくどくスマートではないので、調べてみると「Fisher–Yates shuffle」というアルゴリズムがあるそうです。
要素が5個の場合、5番目の要素とランダムに選んだ要素を交換します。これで5番目の要素は確定。
次に、要素を一つ減らして、4番目の要素と1番目から4番目まででランダムに選んだ要素を交換します。これで4番目の要素は確定。
これを繰り返します。

Array.prototype.shuffle = function() {
  //配列内の要素をランダムに並べ替える。
  //元の配列に影響する。
  //Fisher–Yates shuffle
  var i, j, temp;
  i = this.length;
  while(i) {
    j = Math.floor(Math.random() * i);
    i--;
    temp = this[i];
    this[i] = this[j];
    this[j] = temp;
  }
  return this;
}

この関数は使う箇所よりも前に書きます。また次のように使います。

arr.shuffle();

省略して書く方法

mapを使うとコードが短くなります。ソートを使う方法を省略して書いてみます。

words = ["ワシントン", "アダムズ", "ジェファーソン", "マディソン", "モンロー"];
new_words = words.map((x) => [x, Math.random()]).sort((a, b) => a[1] - b[1]).map((x) => x[0]);

コメント

タイトルとURLをコピーしました