我正在研究如何在JavaScript中随机访问数组元素。我发现了许多相关链接,比如: 从JavaScript数组获取随机项
var item = items[Math.floor(Math.random()*items.length)];
但是在这种情况下,我们只能从数组中选择一个项目。如果我们想要多个元素,该怎么办?如何从数组中获取多个元素?
我正在研究如何在JavaScript中随机访问数组元素。我发现了许多相关链接,比如: 从JavaScript数组获取随机项
var item = items[Math.floor(Math.random()*items.length)];
但是在这种情况下,我们只能从数组中选择一个项目。如果我们想要多个元素,该怎么办?如何从数组中获取多个元素?
只有两行:
// Shuffle array
const shuffled = array.sort(() => 0.5 - Math.random());
// Get sub-array of first n elements after shuffled
let selected = shuffled.slice(0, n);
let random = array.sort(() => .5 - Math.random()).slice(0,n)
。 - unitariofunction getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
Set
(它在'13年还不可用:-/)。 - Bergi这里有一个简短的独特解决方案
array.sort(() => Math.random() - Math.random()).slice(0, n)
Lodash的_.sample
和_.sampleSize
方法可从集合中获取一个或N个不重复键的随机元素,最多取到集合的大小。
_.sample([1, 2, 3, 4]);
// => 2
_.sampleSize([1, 2, 3], 2);
// => [3, 1]
_.sampleSize([1, 2, 3], 3);
// => [2, 3, 1]
_
?它不是标准的Javascript对象。 - vanowm获取 5 个随机元素,而不改变原始数组:
const n = 5;
const sample = items
.map(x => ({ x, r: Math.random() }))
.sort((a, b) => a.r - b.r)
.map(a => a.x)
.slice(0, n);
(请勿用于大列表)
items
数组(第2行),并创建一组成对的数组:原始项和一个随机数(第3行)。然后按照随机数对数组进行排序(第4行)。接着,它再次创建一个简单的项列表,只使用原始项(因此跳过随机数,第5行)。最后,它选择(随机排序的)项目数组的前n
个项目(第6行)。为了更好地理解,请阅读诸如map
和sort
之类的函数的文档,例如https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map。 - Jochem Schulenklopper将 Python 标准库中的 .sample
迁移:
function sample(population, k){
/*
Chooses k unique random elements from a population sequence or set.
Returns a new list containing elements from the population while
leaving the original population unchanged. The resulting list is
in selection order so that all sub-slices will also be valid random
samples. This allows raffle winners (the sample) to be partitioned
into grand prize and second place winners (the subslices).
Members of the population need not be hashable or unique. If the
population contains repeats, then each occurrence is a possible
selection in the sample.
To choose a sample in a range of integers, use range as an argument.
This is especially fast and space efficient for sampling from a
large population: sample(range(10000000), 60)
Sampling without replacement entails tracking either potential
selections (the pool) in a list or previous selections in a set.
When the number of selections is small compared to the
population, then tracking selections is efficient, requiring
only a small set and an occasional reselection. For
a larger number of selections, the pool tracking method is
preferred since the list takes less space than the
set and it doesn't suffer from frequent reselections.
*/
if(!Array.isArray(population))
throw new TypeError("Population must be an array.");
var n = population.length;
if(k < 0 || k > n)
throw new RangeError("Sample larger than population or is negative");
var result = new Array(k);
var setsize = 21; // size of a small set minus size of an empty list
if(k > 5)
setsize += Math.pow(4, Math.ceil(Math.log(k * 3) / Math.log(4)))
if(n <= setsize){
// An n-length list is smaller than a k-length set
var pool = population.slice();
for(var i = 0; i < k; i++){ // invariant: non-selected at [0,n-i)
var j = Math.random() * (n - i) | 0;
result[i] = pool[j];
pool[j] = pool[n - i - 1]; // move non-selected item into vacancy
}
}else{
var selected = new Set();
for(var i = 0; i < k; i++){
var j = Math.random() * n | 0;
while(selected.has(j)){
j = Math.random() * n | 0;
}
selected.add(j);
result[i] = population[j];
}
}
return result;
}
实现源自Lib/random.py。
注:
setsize
基于 Python 特性进行设置以提高效率。虽然它没有为 JavaScript 进行调整,但算法仍将按预期运行。Array.prototype.sort
而不符合 ECMAScript 规范,但此算法保证在有限时间内终止。Set
的旧浏览器,可以使用 Array
替换集合,并将 .has(j)
替换为 .indexOf(j) > -1
。与接受的答案相比的性能:
var getMeRandomElements = function(sourceArray, neededElements) {
var result = [];
for (var i = 0; i < neededElements; i++) {
result.push(sourceArray[Math.floor(Math.random()*sourceArray.length)]);
}
return result;
}
您还应检查源数组中是否有足够的元素可供返回。如果要返回唯一元素,则应从源数组中删除已选择的元素。
sourceArray
中的同一元素。 - Samposplice
从数组中删除已选择的元素:
var items = [1, 2, 3, 4, 5];
var newItems = [];
for (var i = 0; i < 3; i++) {
var idx = Math.floor(Math.random() * items.length);
newItems.push(items[idx]);
items.splice(idx, 1);
}
console.log(newItems);
1
是deleteCount
,表示要删除的旧数组元素数量。(顺便说一下,我将最后两行代码简化为newItems.push(items.splice(idx, 1)[0])
)。Shyam Dixit - Kurt PeekES6语法
const pickRandom = (arr,count) => {
let _arr = [...arr];
return[...Array(count)].map( ()=> _arr.splice(Math.floor(Math.random() * _arr.length), 1)[0] );
}
我简直不敢相信没有人提到过这种方法,非常干净、直截了当。
const getRnd = (a, n) => new Array(n).fill(null).map(() => a[Math.floor(Math.random() * a.length)]);