[javascript] 배열에서 여러 임의의 요소를 얻는 방법은 무엇입니까?

나는 ‘자바 스크립트의 배열에서 무작위로 요소에 액세스하는 방법’을 연구하고 있습니다. 이것에 관한 많은 링크를 찾았습니다. 좋아요 :
JavaScript 배열에서 임의의 항목 가져 오기

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

그러나 여기에서는 배열에서 하나의 항목 만 선택할 수 있습니다. 둘 이상의 요소를 원하면 어떻게 이것을 달성 할 수 있습니까? 배열에서 하나 이상의 요소를 어떻게 얻을 수 있습니까?



답변

다음과 같은 비파괴 ( 빠른 ) 기능을 사용해보십시오 .

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


답변

두 줄 :

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

데모 :


답변

여기에 한 줄짜리 독특한 솔루션이 있습니다.

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


답변

.samplePython 표준 라이브러리에서 포팅 :

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. 그러나이 알고리즘은 한정된 시간 내에 종료됩니다.
  • 하지 않는 이전 브라우저의 Set구현, 설정은로 대체 할 수 Array.has(j)교체 .indexOf(j) > -1.

허용 된 답변에 대한 성능 :


답변

원래 배열을 변경하지 않고 무작위 항목 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);

(큰 목록에는 사용하지 마십시오)


답변

다음과 같은 기능을 만듭니다.

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에 반환 할 요소가 충분한 지 확인해야합니다. 고유 한 요소를 반환하려면 sourceArray에서 선택한 요소를 제거해야합니다.


답변

ES6 구문

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