RubyのsampleをJavascriptで実装する
Twitterで @t4trawがこんな事をいっていた
javascriptで、rubyでいう.sampleって何かあるのか?
— t4traw (@t4traw) August 7, 2019
sampleって確か、配列からランダムに値を取得する奴だよね? Ruby初心者
Javascriptには、そういった便利関数が存在しないので prototype
での実装を薦めた
Array.prototype.sample = function() {
return this[Math.floor(Math.random() * this.length)]
}
let hoge = [1,2,3,4,5];
console.log(hoge.sample());
満足できなかったようで、更に要望(?)がきた
これをhoge.sample(3)とかで出せるようにしたいな
— t4traw (@t4traw) August 7, 2019
それらしい形に修正してみた
Array.prototype.sample = function(n) {
return [...Array(n||1)].map(() => this[Math.floor(Math.random() * this.length)]);
}
let hoge = [1,2,3,4,5];
console.log(hoge.sample());
console.log(hoge.sample(3));
俺も今書いてたけど、かぶっちゃうんだよねー。重複しないように、配列から抜かないといけない pic.twitter.com/PH6aQnPT7b
— t4traw (@t4traw) August 7, 2019
……ここまできて、そもそも Ruby の .sample
ってどんな挙動なんだ?って調べてみた
sample(Array)
sampleメソッドは、配列の要素を1つランダムに返します。配列が空の場合はnilを返します。
引数に整数を指定すると、その数だけ要素をランダムに取り出し配列で返します。
要素の順番もランダムになります。
配列が空のとき、あるいは引数が0のときは空の配列[]を返します。
引数が配列のサイズより大きいときは、配列のサイズだけ要素を取り出します。
……ちゃんと実装すると大変そうなので(ワンライナーで書けない)
次の要件を満たすコードを書いてみた
- 引数が無い場合は 1 として扱う
- 値は重複しないようにする
- 指定された数だけ、ランダムに要素を取り出す
- 配列サイズより大きな値を指定された場合は、配列のサイズだけ要素を取り出す
出来上がったコードがコレです
Array.prototype.sample = function(n) {
return Array.from(this).sort(_=>Math.random()-0.5).splice(0, n>this.length ? this.length : n||1);
}
let hoge = [1,2,3,4,5];
console.log(hoge.sample(3));
console.log(hoge.sample(6));
[ 2, 3, 1 ]
[ 4, 3, 2, 1, 5 ]
配列をシャッフルし、先頭から指定された数の値を取り出す方針に変更した
先までのコードは元の配列(hoge)をぶっ壊してたので複製した配列のデータを扱うようにした Array.from()
改善点
動きそうにみえて、結構改善点がたくさんあります
元のrubyのsample
は、引数の有無で戻り値が配列だったり、整数だったりする
それにならって近づけるのであれば……
sample() … (int)1
sample(0) … []
sample(1) … [1]
sample(3) … [1,2,3]
といった形の実装にしないといけない
上記のコードでは sample() で実行された場合も、配列で値を返し、
sample(0) が指定された場合も 配列でひとつ要素を取出してしまう(欠陥)
ただ、そもそも sample(0) なんて使うシーンがあるのか疑問なので、ここまでで一旦終了とした
Gistってものを初めてしった
こんなのもあったんですね。とりあえずアップしてみました https://gist.github.com/masaquid/e256a5485181f1a270f82f155d3304a0