简体   繁体   English

如何从数组中获取多个随机元素?

[英]How to get a number of random elements from an array?

I am working on 'how to access elements randomly from an array in javascript'.我正在研究“如何从 javascript 中的数组中随机访问元素”。 I found many links regarding this.我发现了很多关于这个的链接。 Like: Get random item from JavaScript array喜欢: 从 JavaScript 数组中获取随机项目

var item = items[Math.floor(Math.random()*items.length)];

But in this, we can choose only one item from the array.但是在这种情况下,我们只能从数组中选择一项。 If we want more than one elements then how can we achieve this?如果我们想要多个元素,那么我们该如何实现呢? How can we get more than one element from an array?我们如何从一个数组中获取多个元素?

Just two lines :只有两行:

// 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);

DEMO :演示

Try this non-destructive (and fast) function:试试这个非破坏性(和快速)的功能:

function 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;
}

这里有一个单行独特的解决方案

 array.sort(() => Math.random() - Math.random()).slice(0, n)

lodash _.sample and _.sampleSize . lodash _.sample_.sampleSize

Gets one or n random elements at unique keys from collection up to the size of collection.从集合的唯一键处获取一个或 n 个随机元素,直到集合的大小。

_.sample([1, 2, 3, 4]);
// => 2

_.sampleSize([1, 2, 3], 2);
// => [3, 1]
 
_.sampleSize([1, 2, 3], 3);
// => [2, 3, 1]

Getting 5 random items without changing the original array:在不改变原始数组的情况下获得 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);

(Don't use this for big lists) (不要将它用于大列表)

create a funcion which does that:创建一个执行此操作的函数:

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;
}

you should also check if the sourceArray has enough elements to be returned.您还应该检查 sourceArray 是否有足够的元素返回。 and if you want unique elements returned, you should remove selected element from the sourceArray.如果要返回唯一元素,则应从 sourceArray 中删除选定元素。

Porting .sample from the Python standard library:从 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;
}

Implementation ported from Lib/random.py .Lib/random.py移植的实现。

Notes:笔记:

  • setsize is set based on characteristics in Python for efficiency.为了提高效率, setsize是根据 Python 中的特性设置的。 Although it has not been adjusted for JavaScript, the algorithm will still function as expected.尽管尚未针对 JavaScript 进行调整,但该算法仍将按预期运行。
  • Some other answers described in this page are not safe according to the ECMAScript specification due to the misuse of Array.prototype.sort .由于滥用Array.prototype.sort ,根据 ECMAScript 规范,本页中描述的其他一些答案是不安全的。 This algorithm however is guaranteed to terminate in finite time.然而,该算法保证在有限时间内终止。
  • For older browsers that do not have Set implemented, the set can be replaced with an Array and .has(j) replaced with .indexOf(j) > -1 .对于没有实现Set的旧浏览器,可以将 set 替换为Array并将.has(j)替换为.indexOf(j) > -1

Performance against the accepted answer:针对公认答案的表现:

If you want to randomly get items from the array in a loop without repetitions you can remove the selected item from the array with splice :如果您想在循环中从数组中随机获取项目而不重复,您可以使用splice从数组中删除所选项目:

 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);

ES6 syntax ES6 语法

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)]);
Array.prototype.getnkill = function() {
    var a = Math.floor(Math.random()*this.length);
    var dead = this[a];
    this.splice(a,1);
    return dead;
}

//.getnkill() removes element in the array 
//so if you like you can keep a copy of the array first:

//var original= items.slice(0); 


var item = items.getnkill();

var anotheritem = items.getnkill();

Here's a nicely typed version.这是一个很好的打字版本。 It doesn't fail.它不会失败。 Returns a shuffled array if sample size is larger than original array's length.如果样本大小大于原始数组的长度,则返回一个混洗数组。

function sampleArr<T>(arr: T[], size: number): T[] {
  const setOfIndexes = new Set<number>();
  while (setOfIndexes.size < size && setOfIndexes.size < arr.length) {
    setOfIndexes.add(randomIntFromInterval(0, arr.length - 1));
  }
  return Array.from(setOfIndexes.values()).map(i => arr[i]);
}

const randomIntFromInterval = (min: number, max: number): number =>
  Math.floor(Math.random() * (max - min + 1) + min);

In this answer, I want to share with you the test that I have to know the best method that gives equal chances for all elements to have random subarray.在这个答案中,我想与您分享我必须知道的最佳方法的测试,该方法使所有元素都有相同的机会具有随机子数组。

Method 01方法01

array.sort(() => Math.random() - Math.random()).slice(0, n)

using this method, some elements have higher chances comparing with others.使用这种方法,与其他元素相比,某些元素具有更高的机会。

 calculateProbability = function(number=0 ,iterations=10000,arraySize=100) { let occ = 0 for (let index = 0; index < iterations; index++) { const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize] /** Wrong Method */ const arr = myArray.sort(function() { return val= .5 - Math.random(); }); if(arr[0]===number) { occ ++ } } console.log("Probability of ",number, " = ",occ*100 /iterations,"%") } calculateProbability(0) calculateProbability(0) calculateProbability(0) calculateProbability(50) calculateProbability(50) calculateProbability(50) calculateProbability(25) calculateProbability(25) calculateProbability(25)

Method 2方法二

Using this method, the elements have the same probability:使用这种方法,元素具有相同的概率:

 const arr = myArray
      .map((a) => ({sort: Math.random(), value: a}))
      .sort((a, b) => a.sort - b.sort)
      .map((a) => a.value)

 calculateProbability = function(number=0 ,iterations=10000,arraySize=100) { let occ = 0 for (let index = 0; index < iterations; index++) { const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize] /** Correct Method */ const arr = myArray .map((a) => ({sort: Math.random(), value: a})) .sort((a, b) => a.sort - b.sort) .map((a) => a.value) if(arr[0]===number) { occ ++ } } console.log("Probability of ",number, " = ",occ*100 /iterations,"%") } calculateProbability(0) calculateProbability(0) calculateProbability(0) calculateProbability(50) calculateProbability(50) calculateProbability(50) calculateProbability(25) calculateProbability(25) calculateProbability(25)

The correct answer is posted in in the following link: https://stackoverflow.com/a/46545530/3811640正确答案发布在以下链接中: https ://stackoverflow.com/a/46545530/3811640

2020 2020
non destructive functional programing style, working in a immutable context.非破坏性的函数式编程风格,在不可变的上下文中工作。

 const _randomslice = (ar, size) => { let new_ar = [...ar]; new_ar.splice(Math.floor(Math.random()*ar.length),1); return ar.length <= (size+1) ? new_ar : _randomslice(new_ar, size); } console.log(_randomslice([1,2,3,4,5],2));

EDIT : This solution is slower than others presented here (which splice the source array) if you want to get only a few elements.编辑:如果您只想获取几个元素,则此解决方案比此处介绍的其他解决方案(拼接源数组)要慢。 The speed of this solution depends only on the number of elements in the original array, while the speed of the splicing solution depends on the number of elements required in the output array.这种解法的速度只取决于原始数组中元素的数量,而拼接解法的速度取决于输出数组中所需的元素数量。

If you want non-repeating random elements, you can shuffle your array then get only as many as you want:如果你想要不重复的随机元素,你可以打乱你的数组,然后只得到你想要的数量:

function shuffle(array) {
    var counter = array.length, temp, index;

    // While there are elements in the array
    while (counter--) {
        // Pick a random index
        index = (Math.random() * counter) | 0;

        // And swap the last element with it
        temp = array[counter];
        array[counter] = array[index];
        array[index] = temp;
    }

    return array;
}

var arr = [0,1,2,3,4,5,7,8,9];

var randoms = shuffle(arr.slice(0)); // array is cloned so it won't be destroyed
randoms.length = 4; // get 4 random elements

DEMO: http://jsbin.com/UHUHuqi/1/edit演示:http: //jsbin.com/UHUHuqi/1/edit

Shuffle function taken from here: https://stackoverflow.com/a/6274398/1669279从这里获取的随机播放功能: https ://stackoverflow.com/a/6274398/1669279

I needed a function to solve this kind of issue so I'm sharing it here.我需要一个函数来解决这类问题,所以我在这里分享它。

    const getRandomItem = function(arr) {
        return arr[Math.floor(Math.random() * arr.length)];
    }

    // original array
    let arr = [4, 3, 1, 6, 9, 8, 5];

    // number of random elements to get from arr
    let n = 4;

    let count = 0;
    // new array to push random item in
    let randomItems = []
    do {
        let item = getRandomItem(arr);
        randomItems.push(item);
        // update the original array and remove the recently pushed item
        arr.splice(arr.indexOf(item), 1);
        count++;
    } while(count < n);

    console.log(randomItems);
    console.log(arr);

Note: if n = arr.length then basically you're shuffling the array arr and randomItems returns that shuffled array.注意:如果n = arr.length那么基本上你是在洗牌数组arrrandomItems返回那个洗牌的数组。

Demo演示

Here's an optimized version of the code ported from Python by @Derek, with the added destructive (in-place) option that makes it the fastest algorithm possible if you can go with it.这是@Derek 从 Python 移植的代码的优化版本,添加了破坏性(就地)选项,如果您可以使用它,它可以使其成为最快的算法。 Otherwise it either makes a full copy or, for a small number of items requested from a large array, switches to a selection-based algorithm.否则,它要么制作完整副本,要么对于从大数组请求的少量项目,切换到基于选择的算法。

// Chooses k unique random elements from pool.
function sample(pool, k, destructive) {
    var n = pool.length;
    
    if (k < 0 || k > n)
        throw new RangeError("Sample larger than population or is negative");
    
    if (destructive || n <= (k <= 5 ? 21 : 21 + Math.pow(4, Math.ceil(Math.log(k*3) / Math.log(4))))) {
        if (!destructive)
            pool = Array.prototype.slice.call(pool);
        for (var i = 0; i < k; i++) { // invariant: non-selected at [i,n)
            var j = i + Math.random() * (n - i) | 0;
            var x = pool[i];
            pool[i] = pool[j];
            pool[j] = x;
        }
        pool.length = k; // truncate
        return pool;
    } else {
        var selected = new Set();
        while (selected.add(Math.random() * n | 0).size < k) {}
        return Array.prototype.map.call(selected, i => pool[i]);
    }
}

In comparison to Derek's implementation, the first algorithm is much faster in Firefox while being a bit slower in Chrome, although now it has the destructive option - the most performant one.与 Derek 的实现相比,第一个算法在 Firefox 中要快得多,而在 Chrome 中要慢一些,尽管现在它具有破坏性选项 - 性能最高的选项。 The second algorithm is simply 5-15% faster.第二种算法只是快了 5-15%。 I try not to give any concrete numbers since they vary depending on k and n and probably won't mean anything in the future with the new browser versions.我尽量不给出任何具体数字,因为它们会因 k 和 n 的不同而有所不同,并且可能在未来对新的浏览器版本没有任何意义。

The heuristic that makes the choice between algorithms originates from Python code.在算法之间进行选择的启发式方法源自 Python 代码。 I've left it as is, although it sometimes selects the slower one.我保持原样,尽管它有时会选择较慢的那个。 It should be optimized for JS, but it's a complex task since the performance of corner cases is browser- and their version-dependent.它应该针对 JS 进行优化,但这是一项复杂的任务,因为极端情况的性能取决于浏览器及其版本。 For example, when you try to select 20 out of 1000 or 1050, it will switch to the first or the second algorithm accordingly.例如,当您尝试从 1000 或 1050 中选择 20 时,它将相应地切换到第一种或第二种算法。 In this case the first one runs 2x faster than the second one in Chrome 80 but 3x slower in Firefox 74.在这种情况下,第一个在 Chrome 80 中的运行速度比第二个快 2 倍,但在 Firefox 74 中慢 3 倍。

Sampling with possible duplicates :采样可能重复

const sample_with_duplicates = Array(sample_size).fill().map(() => items[~~(Math.random() * items.length)])

Sampling without duplicates :无重复抽样:

const sample_without_duplicates = [...Array(items.length).keys()].sort(() => 0.5 - Math.random()).slice(0, sample_size).map(index => items[index]);

Since without duplicates requires sorting the whole index array first, it is considerably slow than with possible duplicates for big items input arrays.由于没有重复项需要首先对整个索引数组进行排序,因此它比大items输入数组可能存在重复项要慢得多。

Obviously, the max size of without duplicates is <= items.length显然,没有重复的最大尺寸是 <= items.length

Check this fiddle: https://jsfiddle.net/doleron/5zw2vequ/30/检查这个小提琴: https ://jsfiddle.net/doleron/5zw2vequ/30/

It extracts random elements from srcArray one by one while it get's enough or there is no more elements in srcArray left for extracting.它从 srcArray 中一个一个地提取随机元素,而它已经足够了,或者 srcArray 中没有更多的元素可供提取。 Fast and reliable.快速可靠。

 function getNRandomValuesFromArray(srcArr, n) { // making copy to do not affect original srcArray srcArr = srcArr.slice(); resultArr = []; // while srcArray isn't empty AND we didn't enough random elements while (srcArr.length && resultArr.length < n) { // remove one element from random position and add this element to the result array resultArr = resultArr.concat( // merge arrays srcArr.splice( // extract one random element Math.floor(Math.random() * srcArr.length), 1 ) ); } return resultArr; }

Here's a function I use that allows you to easily sample an array with or without replacement:这是我使用的一个函数,可让您轻松地对数组进行采样,无论是否替换:

  // Returns a random sample (either with or without replacement) from an array
  const randomSample = (arr, k, withReplacement = false) => {
    let sample;
    if (withReplacement === true) {  // sample with replacement
      sample = Array.from({length: k}, () => arr[Math.floor(Math.random() *  arr.length)]);
    } else { // sample without replacement
      if (k > arr.length) {
        throw new RangeError('Sample size must be less than or equal to array length         when sampling without replacement.')
      }
      sample = arr.map(a => [a, Math.random()]).sort((a, b) => {
        return a[1] < b[1] ? -1 : 1;}).slice(0, k).map(a => a[0]); 
      };
    return sample;
  };

Using it is simple:使用它很简单:

Without Replacement (default behavior)没有替换(默认行为)

randomSample([1, 2, 3], 2) may return [2, 1] randomSample([1, 2, 3], 2)可能返回[2, 1]

With Replacement更换

randomSample([1, 2, 3, 4, 5, 6], 4) may return [2, 3, 3, 2] randomSample([1, 2, 3, 4, 5, 6], 4)可能返回[2, 3, 3, 2]

var getRandomElements = function(sourceArray, requiredLength) {
    var result = [];
    while(result.length<requiredLength){
        random = Math.floor(Math.random()*sourceArray.length);
        if(result.indexOf(sourceArray[random])==-1){
            result.push(sourceArray[random]);
        }
    }
    return result;
}
const items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'I', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1, 2, 3, 4, 5];

const fetchRandomArray = ({pool=[], limit=1})=>{
let query = []
let selectedIndices = {}

while(query.length < limit){
    const index = Math.floor(Math.random()*pool.length)
    if(typeof(selectedIndices[index])==='undefined'){
        query.push(items[index])
        selectedIndices[index] = index
    }
}

console.log(fetchRandomArray({pool:items, limit:10})

Here is the most correct answer and it will give you Random + Unique elements.这是最正确的答案,它会给你随机+独特的元素。

function randomize(array, n)
{
    var final = [];
    array = array.filter(function(elem, index, self) {
        return index == self.indexOf(elem);
    }).sort(function() { return 0.5 - Math.random() });

    var len = array.length,
    n = n > len ? len : n;

    for(var i = 0; i < n; i ++)
    {
        final[i] = array[i];
    }

    return final;
}

// randomize([1,2,3,4,5,3,2], 4);
// Result: [1, 2, 3, 5] // Something like this

2019 2019

This is same as Laurynas Mališauskas answer, just that the elements are unique (no duplicates).这与Laurynas Mališauskas的回答相同,只是元素是唯一的(没有重复)。

var getMeRandomElements = function(sourceArray, neededElements) {
    var result = [];
    for (var i = 0; i < neededElements; i++) {
    var index = Math.floor(Math.random() * sourceArray.length);
        result.push(sourceArray[index]);
        sourceArray.splice(index, 1);
    }
    return result;
}

Now to answer original question " How to get multiple random elements by jQuery ", here you go:现在回答原始问题“如何通过jQuery获取多个随机元素”,你去:

var getMeRandomElements = function(sourceArray, neededElements) {
    var result = [];
    for (var i = 0; i < neededElements; i++) {
    var index = Math.floor(Math.random() * sourceArray.length);
        result.push(sourceArray[index]);
        sourceArray.splice(index, 1);
    }
    return result;
}

var $set = $('.someClass');// <<<<< change this please

var allIndexes = [];
for(var i = 0; i < $set.length; ++i) {
    allIndexes.push(i);
}

var totalRandom = 4;// <<<<< change this please
var randomIndexes = getMeRandomElements(allIndexes, totalRandom);

var $randomElements = null;
for(var i = 0; i < randomIndexes.length; ++i) {
    var randomIndex = randomIndexes[i];
    if($randomElements === null) {
        $randomElements = $set.eq(randomIndex);
    } else {
        $randomElements.add($set.eq(randomIndex));
    }
}

// $randomElements is ready
$randomElements.css('backgroundColor', 'red');

items.sort(() => (Math.random() > 0.5 ? 1 : -1)).slice(0, count);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM