指定範囲からランダムで一意なn個の値を取得する
const pickN = (min, max, n) => {
const list = new Array(max-min+1).fill().map((_, i) => i + min);
const ret = [];
while(n--) {
const rand = Math.floor(Math.random() * (list.length + 1)) - 1;
ret.push(...list.splice(rand, 1))
}
return ret;
}
const list1 = pickN(1, 100, 30);
console.log(list1);
// [47, 69, 19, 73, 27, 35, 68, 21, 88, 41, 86, 14, 50, 42, 94, 26, 2, 43, 83, 76, 57, 31, 97, 45, 84, 99, 46, 22, 9, 81]
const list2 = pickN(500, 800, 10);
console.log(list2);
// [714, 557, 523, 760, 750, 677, 632, 566, 798, 587]
new Array(max - min + 1).fill().map((_, i) => i + min)
は、min〜mixまでの値が入った配列を生成している。そして、ret.push(...list.splice(rand, 1))
でさきほどの配列からランダムな位置の1つの値を取得し、結果用の配列に追加している。ワンライナーで書く方法
前述のコードを社内のSlackに載せたところ、凄腕エンジニアの方から「効率無視で超短いやつ」というメッセージとともにワンライナーが投稿された。
[...Array(100).keys()].map(v=>v+1).sort(_=>Math.random()-.5).slice(0, 50)
初見では何しているのか理解できなかった。
このワンライナーを理解するには、
Array.prototype.sort
とMath.random
について知る必要がある。Array.prototype.sort
Array.prototype.sort(compareFunction(a, b))
の仕様は以下の通り。- compareFunction(a, b)の結果が0未満: [a, b] = [a, b] (そのまま)
- compareFunction(a, b)の結果が0: 変更せず
- compareFunction(a, b)の結果が0より大きい: [b, a] = [a, b] (aとbを入れ替える)
Math.random()
Math.random()
の仕様は、0 <= n < 1
の範囲で浮動小数点の疑似乱数を返す。
Math.random() - 0.5
というのは-0.5 <= n < 0.5
の値がランダムで取得できる。
配列内をシャッフルする
この2つを組み合わせて、
arr.sort(_ => Math.random() - 0.5).splice(0, 50)
を実行すると配列の中身をシャッフルし、その先頭50個を取得できるというわけだ。
参考サイト
以上
written by @bc_rikko
0 件のコメント :
コメントを投稿