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

Shy*_*xit 66 html javascript arrays jquery

我正在研究'如何在javascript中从数组中随机访问元素'.我找到了很多相关的链接.喜欢: 从JavaScript数组中获取随机项

var item = items[Math.floor(Math.random()*items.length)];
Run Code Online (Sandbox Code Playgroud)

问题:但是在这里我们只能从数组中选择一个项目.如果我们想要多个元素,那么我们怎样才能实现这个.所以请从这个语句中我们如何从数组中获得多个元素.

Abd*_*UMI 128

只有两行:

// 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);
Run Code Online (Sandbox Code Playgroud)

演示:

  • 这很好,但远非随机.第一项比最后一项更有机会被选中.请参阅此处原因:/sf/answers/1305511861/ (19认同)
  • 非常好!当然也可以使用一个班轮:`let random = array.sort(()=> .5 - Math.random()).slice(0,n)` (11认同)
  • 这很好,但对于大型阵列来说会占用 CPU 资源;如果只选取几个元素,为什么需要对整个数组进行排序? (7认同)
  • 惊人的!如果你想保持数组完整,你可以像这样改变第一行: const shuffled = [...array].sort(() => 0.5 - Math.random()); (6认同)
  • 天才!优雅,简短,简单,快速,使用内置功能。 (2认同)

Ber*_*rgi 60

尝试这种非破坏性(和快速)功能:

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 作者说(在评论中)他想避免重复.这会产生重复. (3认同)
  • 嘿,我只想说我花了大约十分钟欣赏这个算法的美妙. (2认同)
  • @cbdev420 是的,这只是一个(部分)fisher-yates 洗牌 (2认同)

Ola*_*nle 27

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

 array.sort(() => Math.random() - Math.random()).slice(0, n)
Run Code Online (Sandbox Code Playgroud)

  • 虽然它有效,但对于更大的阵列来说也可能会很慢。 (2认同)
  • 这不会给你带来均匀的分布。请参阅 https://www.robweir.com/blog/2010/02/microsoft-random-browser-ballot.html (2认同)

Lau*_*kas 11

创建一个功能:

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;
}
Run Code Online (Sandbox Code Playgroud)

您还应该检查sourceArray是否有足够的元素要返回.如果要返回唯一元素,则应从sourceArray中删除所选元素.


nod*_*ejh 10

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]
Run Code Online (Sandbox Code Playgroud)

  • @vanowm 它是 lodash,通常使用“_”别名导入。 (4认同)

pom*_*ber 9

在不更改原始数组的情况下获取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);
Run Code Online (Sandbox Code Playgroud)

(不要将此用于大型列表)


Ror*_*san 7

如果您想在循环中从数组中随机获取项目而不重复,您可以使用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);
Run Code Online (Sandbox Code Playgroud)

  • [Shyam Dixit](/sf/users/173935191/),根据 [MDN 文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/ Reference/Global_Objects/Array/splice)“1”是“deleteCount”,表示要删除的旧数组元素的数量。(顺便说一句,我将最后两行缩减为 `newItems.push(items.splice(idx, 1)[0])`)。 (2认同)

Yai*_*evy 7

ES6 语法

const pickRandom = (arr,count) => {
  let _arr = [...arr];
  return[...Array(count)].map( ()=> _arr.splice(Math.floor(Math.random() * _arr.length), 1)[0] ); 
}
Run Code Online (Sandbox Code Playgroud)


Der*_*會功夫 6

.sample从Python标准库移植:

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, 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 - i) | 0;
            while(selected.has(j)){
                j = Math.random() * (n - i) | 0;
            }
            selected.add(j);
            result[i] = population[j];
        }
    }

    return result;
}
Run Code Online (Sandbox Code Playgroud)

Lib/random.py移植的实现.

笔记:

  • setsize基于Python中的特性来设置效率.虽然它尚未针对JavaScript进行调整,但算法仍将按预期运行.
  • 由于误用,根据ECMAScript规范,本页描述的其他一些答案并不安全Array.prototype.sort.然而,该算法保证在有限时间内终止.
  • 对于尚未Set实现的旧版浏览器,该集可以替换为a Array.has(j)替换为.indexOf(j) > -1.

针对公认答案的表现:


Ded*_*Dev 5

我不敢相信没有人没有提到这个方法,非常干净和简单。

const getRnd = (a, n) => new Array(n).fill(null).map(() => a[Math.floor(Math.random() * a.length)]);
Run Code Online (Sandbox Code Playgroud)

  • 您无法确保两个项目不会重复。 (4认同)