Sampling a random subset from an array










18















What is a clean way of taking a random sample, without replacement from an array in javascript? So suppose there is an array



x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]


and I want to randomly sample 5 unique values; i.e. generate a random subset of length 5. To generate one random sample one could do something like:



x[Math.floor(Math.random()*x.length)];


But if this is done multiple times, there is a risk of a grabbing the same entry multiple times.










share|improve this question
























  • I noticed Avi Moondra's bl.ock uses this technique

    – The Red Pea
    Aug 27 '16 at 3:36















18















What is a clean way of taking a random sample, without replacement from an array in javascript? So suppose there is an array



x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]


and I want to randomly sample 5 unique values; i.e. generate a random subset of length 5. To generate one random sample one could do something like:



x[Math.floor(Math.random()*x.length)];


But if this is done multiple times, there is a risk of a grabbing the same entry multiple times.










share|improve this question
























  • I noticed Avi Moondra's bl.ock uses this technique

    – The Red Pea
    Aug 27 '16 at 3:36













18












18








18


8






What is a clean way of taking a random sample, without replacement from an array in javascript? So suppose there is an array



x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]


and I want to randomly sample 5 unique values; i.e. generate a random subset of length 5. To generate one random sample one could do something like:



x[Math.floor(Math.random()*x.length)];


But if this is done multiple times, there is a risk of a grabbing the same entry multiple times.










share|improve this question
















What is a clean way of taking a random sample, without replacement from an array in javascript? So suppose there is an array



x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]


and I want to randomly sample 5 unique values; i.e. generate a random subset of length 5. To generate one random sample one could do something like:



x[Math.floor(Math.random()*x.length)];


But if this is done multiple times, there is a risk of a grabbing the same entry multiple times.







javascript arrays random numerical-methods






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Aug 13 '12 at 14:04









Zheileman

2,2991620




2,2991620










asked Aug 13 '12 at 13:25









JeroenJeroen

16.3k3093160




16.3k3093160












  • I noticed Avi Moondra's bl.ock uses this technique

    – The Red Pea
    Aug 27 '16 at 3:36

















  • I noticed Avi Moondra's bl.ock uses this technique

    – The Red Pea
    Aug 27 '16 at 3:36
















I noticed Avi Moondra's bl.ock uses this technique

– The Red Pea
Aug 27 '16 at 3:36





I noticed Avi Moondra's bl.ock uses this technique

– The Red Pea
Aug 27 '16 at 3:36












10 Answers
10






active

oldest

votes


















38














I suggest shuffling a copy of the array using the Fisher-Yates shuffle and taking a slice:



function getRandomSubarray(arr, size) 
var shuffled = arr.slice(0), i = arr.length, temp, index;
while (i--)
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;

return shuffled.slice(0, size);


var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var fiveRandomMembers = getRandomSubarray(x, 5);


Note that this will not be the most efficient method for getting a small random subset of a large array because it shuffles the whole array unnecessarily. For better performance you could do a partial shuffle instead:



function getRandomSubarray(arr, size) 
var shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index;
while (i-- > min)
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;

return shuffled.slice(min);






share|improve this answer




















  • 1





    underscore.js uses a "modern version" of the Fisher-Yates shuffle

    – davidDavidson
    Feb 17 '16 at 11:28











  • It should be i* Math.random() instead of (i+1) * Math.random(). Math.random() * (i+1) can return i after Math.floor. And arr[i] will result in index out of bound when i==arr.length

    – Aaron Jo
    Nov 8 '17 at 1:11












  • @AaronJo: No, that's deliberate. i has already been decremented when index is calculated so on the first iteration i + 1 is equal to arr.length in the first function, which is correct.

    – Tim Down
    Nov 8 '17 at 10:15


















11














A little late to the party but this could be solved with underscore's new sample method (underscore 1.5.2 - Sept 2013):



var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

var randomFiveNumbers = _.sample(x, 5);





share|improve this answer




















  • 1





    This produces only 1 element for me, not 5.

    – Snowman
    Aug 21 '16 at 16:02











  • From underscore's documentation: "Produce a random sample from the list. Pass a number to return n random elements from the list. Otherwise a single random item will be returned." - Did you pass in the second parameter?

    – alengel
    Aug 22 '16 at 11:18












  • lodash has a _.sampleSize that works as described above: lodash.com/docs/4.17.4#sampleSize

    – tonyg
    Dec 20 '17 at 17:27


















6














Or... if you use underscore.js...



_und = require('underscore');

...

function sample(a, n)
return _und.take(_und.shuffle(a), n);



Simple enough.






share|improve this answer
































    2














    You can remove the elements from a copy of the array as you select them. Performance is probably not ideal, but it might be OK for what you need:



    function getRandom(arr, size) 
    var copy = arr.slice(0), rand = ;
    for (var i = 0; i < size && i < copy.length; i++)
    var index = Math.floor(Math.random() * copy.length);
    rand.push(copy.splice(index, 1)[0]);

    return rand;






    share|improve this answer






























      2














      While I strongly support using the Fisher-Yates Shuffle, as suggested by Tim Down, here's a very short method for achieving a random subset as requested, mathematically correct, including the empty set, and the given set itself.



      Note solution depends on lodash / underscore:



      function subset(arr) 
      return _.sample(arr, _.random(arr.length));






      share|improve this answer
































        2














        In my opinion, I do not think shuffling the entire deck necessary. You just need to make sure your sample is random not your deck. What you can do, is select the size amount from the front then swap each one in the sampling array with another position in it. So, if you allow replacement you get more and more shuffled.



        function getRandom(length) return Math.floor(Math.random()*(length)); 

        function getRandomSample(array, size)
        var length = array.length;

        for(var i = size; i--;)
        var index = getRandom(length);
        var temp = array[index];
        array[index] = array[i];
        array[i] = temp;


        return array.slice(0, size);



        This algorithm is only 2*size steps, if you include the slice method, to select the random sample.




        More Random



        To make the sample more random, we can randomly select the starting point of the sample. But it is a little more expensive to get the sample.



        function getRandomSample(array, size) 
        var length = array.length, start = getRandom(length);

        for(var i = size; i--;)
        var index = (start + i)%length, rindex = getRandom(length);
        var temp = array[rindex];
        array[rindex] = array[index];
        array[index] = temp;

        var end = start + size, sample = array.slice(start, end);
        if(end > length)
        sample = sample.concat(array.slice(0, end - length));
        return sample;



        What makes this more random is the fact that when you always just shuffling the front items you tend to not get them very often in the sample if the sampling array is large and the sample is small. This would not be a problem if the array was not supposed to always be the same. So, what this method does is change up this position where the shuffled region starts.




        No Replacement



        To not have to copy the sampling array and not worry about replacement, you can do the following but it does give you 3*size vs the 2*size.



        function getRandomSample(array, size) 
        var length = array.length, swaps = , i = size, temp;

        while(i--)
        var rindex = getRandom(length);
        temp = array[rindex];
        array[rindex] = array[i];
        array[i] = temp;
        swaps.push( from: i, to: rindex );


        var sample = array.slice(0, size);

        // Put everything back.
        i = size;
        while(i--)
        var pop = swaps.pop();
        temp = array[pop.from];
        array[pop.from] = array[pop.to];
        array[pop.to] = temp;


        return sample;




        No Replacement and More Random



        To apply the algorithm that gave a little bit more random samples to the no replacement function:



        function getRandomSample(array, size) 
        var length = array.length, start = getRandom(length),
        swaps = , i = size, temp;

        while(i--)
        var index = (start + i)%length, rindex = getRandom(length);
        temp = array[rindex];
        array[rindex] = array[index];
        array[index] = temp;
        swaps.push( from: index, to: rindex );


        var end = start + size, sample = array.slice(start, end);
        if(end > length)
        sample = sample.concat(array.slice(0, end - length));

        // Put everything back.
        i = size;
        while(i--)
        var pop = swaps.pop();
        temp = array[pop.from];
        array[pop.from] = array[pop.to];
        array[pop.to] = temp;


        return sample;




        Faster...



        Like all of these post, this uses the Fisher-Yates Shuffle. But, I removed the over head of copying the array.



        function getRandomSample(array, size) 
        var r, i = array.length, end = i - size, temp, swaps = getRandomSample.swaps;

        while (i-- > end)
        r = getRandom(i + 1);
        temp = array[r];
        array[r] = array[i];
        array[i] = temp;
        swaps.push(i);
        swaps.push(r);


        var sample = array.slice(end);

        while(size--)
        i = swaps.pop();
        r = swaps.pop();
        temp = array[i];
        array[i] = array[r];
        array[r] = temp;


        return sample;

        getRandomSample.swaps = ;





        share|improve this answer
































          2














          If you're using lodash the API changed in 4.x:



          const oneItem = _.sample(arr);
          const nItems = _.sampleSize(arr, n);


          https://lodash.com/docs#sampleSize






          share|improve this answer






























            1














            Here is another implementation based on Fisher-Yater Shuffle. But this one is optimized for the case where the sample size is significantly smaller than the array length. This implementation doesn't scan the entire array nor allocates arrays as large as the original array. It uses sparse arrays to reduce memory allocation.



            function getRandomSample(array, count) 
            var indices = ;
            var result = new Array(count);
            for (let i = 0; i < count; i++ )
            let j = Math.floor(Math.random() * (array.length - i) + i);
            result[i] = array[indices[j] === undefined ? j : indices[j]];
            indices[j] = indices[i] === undefined ? i : indices[i];

            return result;






            share|improve this answer























            • I don't how this works, but it does -- and it's much more efficient when count << array.length. To make it completely generic (i.e., when count is equal or greater than array length) I added: ` let val = array[indices[j] === undefined ? j : indices[j]]; if (val === undefined) result.length = i; break; result[i] = val; ` to force result.length <= array.length, otherwise will get bunch of undefineds in the result.

              – Moos
              Oct 31 '18 at 23:15



















            1














            You can get a 5 elements sample by this way:



            var sample = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
            .map(a => [a,Math.random()])
            .sort((a,b) => return a[1] < b[1] ? -1 : 1;)
            .slice(0,5)
            .map(a => a[0]);


            You can define it as a function to use in your code:



            var randomSample = function(arr,num) return arr.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).slice(0,num).map(a => a[0]); 


            Or add it to the Array object itself:



            Array.prototype.sample = function(num) return this.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).slice(0,num).map(a => a[0]); ;


            if you want, you can separate the code for to have 2 functionalities (Shuffle and Sample):



            Array.prototype.shuffle = function() return this.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).map(a => a[0]); ;
            Array.prototype.sample = function(num) return this.shuffle().slice(0,num); ;





            share|improve this answer






























              0














              Perhaps I am missing something, but it seems there is a solution that does not require the complexity or potential overhead of a shuffle:



              function sample(array,size) 
              const results = ,
              sampled = ;
              while(results.length<size && results.length<array.length)
              const index = Math.trunc(Math.random() * array.length);
              if(!sampled[index])
              results.push(array[index]);
              sampled[index] = true;


              return results;






              share|improve this answer






















                Your Answer






                StackExchange.ifUsing("editor", function ()
                StackExchange.using("externalEditor", function ()
                StackExchange.using("snippets", function ()
                StackExchange.snippets.init();
                );
                );
                , "code-snippets");

                StackExchange.ready(function()
                var channelOptions =
                tags: "".split(" "),
                id: "1"
                ;
                initTagRenderer("".split(" "), "".split(" "), channelOptions);

                StackExchange.using("externalEditor", function()
                // Have to fire editor after snippets, if snippets enabled
                if (StackExchange.settings.snippets.snippetsEnabled)
                StackExchange.using("snippets", function()
                createEditor();
                );

                else
                createEditor();

                );

                function createEditor()
                StackExchange.prepareEditor(
                heartbeatType: 'answer',
                autoActivateHeartbeat: false,
                convertImagesToLinks: true,
                noModals: true,
                showLowRepImageUploadWarning: true,
                reputationToPostImages: 10,
                bindNavPrevention: true,
                postfix: "",
                imageUploader:
                brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                allowUrls: true
                ,
                onDemand: true,
                discardSelector: ".discard-answer"
                ,immediatelyShowMarkdownHelp:true
                );



                );













                draft saved

                draft discarded


















                StackExchange.ready(
                function ()
                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f11935175%2fsampling-a-random-subset-from-an-array%23new-answer', 'question_page');

                );

                Post as a guest















                Required, but never shown

























                10 Answers
                10






                active

                oldest

                votes








                10 Answers
                10






                active

                oldest

                votes









                active

                oldest

                votes






                active

                oldest

                votes









                38














                I suggest shuffling a copy of the array using the Fisher-Yates shuffle and taking a slice:



                function getRandomSubarray(arr, size) 
                var shuffled = arr.slice(0), i = arr.length, temp, index;
                while (i--)
                index = Math.floor((i + 1) * Math.random());
                temp = shuffled[index];
                shuffled[index] = shuffled[i];
                shuffled[i] = temp;

                return shuffled.slice(0, size);


                var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
                var fiveRandomMembers = getRandomSubarray(x, 5);


                Note that this will not be the most efficient method for getting a small random subset of a large array because it shuffles the whole array unnecessarily. For better performance you could do a partial shuffle instead:



                function getRandomSubarray(arr, size) 
                var shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index;
                while (i-- > min)
                index = Math.floor((i + 1) * Math.random());
                temp = shuffled[index];
                shuffled[index] = shuffled[i];
                shuffled[i] = temp;

                return shuffled.slice(min);






                share|improve this answer




















                • 1





                  underscore.js uses a "modern version" of the Fisher-Yates shuffle

                  – davidDavidson
                  Feb 17 '16 at 11:28











                • It should be i* Math.random() instead of (i+1) * Math.random(). Math.random() * (i+1) can return i after Math.floor. And arr[i] will result in index out of bound when i==arr.length

                  – Aaron Jo
                  Nov 8 '17 at 1:11












                • @AaronJo: No, that's deliberate. i has already been decremented when index is calculated so on the first iteration i + 1 is equal to arr.length in the first function, which is correct.

                  – Tim Down
                  Nov 8 '17 at 10:15















                38














                I suggest shuffling a copy of the array using the Fisher-Yates shuffle and taking a slice:



                function getRandomSubarray(arr, size) 
                var shuffled = arr.slice(0), i = arr.length, temp, index;
                while (i--)
                index = Math.floor((i + 1) * Math.random());
                temp = shuffled[index];
                shuffled[index] = shuffled[i];
                shuffled[i] = temp;

                return shuffled.slice(0, size);


                var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
                var fiveRandomMembers = getRandomSubarray(x, 5);


                Note that this will not be the most efficient method for getting a small random subset of a large array because it shuffles the whole array unnecessarily. For better performance you could do a partial shuffle instead:



                function getRandomSubarray(arr, size) 
                var shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index;
                while (i-- > min)
                index = Math.floor((i + 1) * Math.random());
                temp = shuffled[index];
                shuffled[index] = shuffled[i];
                shuffled[i] = temp;

                return shuffled.slice(min);






                share|improve this answer




















                • 1





                  underscore.js uses a "modern version" of the Fisher-Yates shuffle

                  – davidDavidson
                  Feb 17 '16 at 11:28











                • It should be i* Math.random() instead of (i+1) * Math.random(). Math.random() * (i+1) can return i after Math.floor. And arr[i] will result in index out of bound when i==arr.length

                  – Aaron Jo
                  Nov 8 '17 at 1:11












                • @AaronJo: No, that's deliberate. i has already been decremented when index is calculated so on the first iteration i + 1 is equal to arr.length in the first function, which is correct.

                  – Tim Down
                  Nov 8 '17 at 10:15













                38












                38








                38







                I suggest shuffling a copy of the array using the Fisher-Yates shuffle and taking a slice:



                function getRandomSubarray(arr, size) 
                var shuffled = arr.slice(0), i = arr.length, temp, index;
                while (i--)
                index = Math.floor((i + 1) * Math.random());
                temp = shuffled[index];
                shuffled[index] = shuffled[i];
                shuffled[i] = temp;

                return shuffled.slice(0, size);


                var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
                var fiveRandomMembers = getRandomSubarray(x, 5);


                Note that this will not be the most efficient method for getting a small random subset of a large array because it shuffles the whole array unnecessarily. For better performance you could do a partial shuffle instead:



                function getRandomSubarray(arr, size) 
                var shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index;
                while (i-- > min)
                index = Math.floor((i + 1) * Math.random());
                temp = shuffled[index];
                shuffled[index] = shuffled[i];
                shuffled[i] = temp;

                return shuffled.slice(min);






                share|improve this answer















                I suggest shuffling a copy of the array using the Fisher-Yates shuffle and taking a slice:



                function getRandomSubarray(arr, size) 
                var shuffled = arr.slice(0), i = arr.length, temp, index;
                while (i--)
                index = Math.floor((i + 1) * Math.random());
                temp = shuffled[index];
                shuffled[index] = shuffled[i];
                shuffled[i] = temp;

                return shuffled.slice(0, size);


                var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
                var fiveRandomMembers = getRandomSubarray(x, 5);


                Note that this will not be the most efficient method for getting a small random subset of a large array because it shuffles the whole array unnecessarily. For better performance you could do a partial shuffle instead:



                function getRandomSubarray(arr, size) 
                var shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index;
                while (i-- > min)
                index = Math.floor((i + 1) * Math.random());
                temp = shuffled[index];
                shuffled[index] = shuffled[i];
                shuffled[i] = temp;

                return shuffled.slice(min);







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Oct 21 '14 at 10:27

























                answered Aug 13 '12 at 13:30









                Tim DownTim Down

                252k56380467




                252k56380467







                • 1





                  underscore.js uses a "modern version" of the Fisher-Yates shuffle

                  – davidDavidson
                  Feb 17 '16 at 11:28











                • It should be i* Math.random() instead of (i+1) * Math.random(). Math.random() * (i+1) can return i after Math.floor. And arr[i] will result in index out of bound when i==arr.length

                  – Aaron Jo
                  Nov 8 '17 at 1:11












                • @AaronJo: No, that's deliberate. i has already been decremented when index is calculated so on the first iteration i + 1 is equal to arr.length in the first function, which is correct.

                  – Tim Down
                  Nov 8 '17 at 10:15












                • 1





                  underscore.js uses a "modern version" of the Fisher-Yates shuffle

                  – davidDavidson
                  Feb 17 '16 at 11:28











                • It should be i* Math.random() instead of (i+1) * Math.random(). Math.random() * (i+1) can return i after Math.floor. And arr[i] will result in index out of bound when i==arr.length

                  – Aaron Jo
                  Nov 8 '17 at 1:11












                • @AaronJo: No, that's deliberate. i has already been decremented when index is calculated so on the first iteration i + 1 is equal to arr.length in the first function, which is correct.

                  – Tim Down
                  Nov 8 '17 at 10:15







                1




                1





                underscore.js uses a "modern version" of the Fisher-Yates shuffle

                – davidDavidson
                Feb 17 '16 at 11:28





                underscore.js uses a "modern version" of the Fisher-Yates shuffle

                – davidDavidson
                Feb 17 '16 at 11:28













                It should be i* Math.random() instead of (i+1) * Math.random(). Math.random() * (i+1) can return i after Math.floor. And arr[i] will result in index out of bound when i==arr.length

                – Aaron Jo
                Nov 8 '17 at 1:11






                It should be i* Math.random() instead of (i+1) * Math.random(). Math.random() * (i+1) can return i after Math.floor. And arr[i] will result in index out of bound when i==arr.length

                – Aaron Jo
                Nov 8 '17 at 1:11














                @AaronJo: No, that's deliberate. i has already been decremented when index is calculated so on the first iteration i + 1 is equal to arr.length in the first function, which is correct.

                – Tim Down
                Nov 8 '17 at 10:15





                @AaronJo: No, that's deliberate. i has already been decremented when index is calculated so on the first iteration i + 1 is equal to arr.length in the first function, which is correct.

                – Tim Down
                Nov 8 '17 at 10:15













                11














                A little late to the party but this could be solved with underscore's new sample method (underscore 1.5.2 - Sept 2013):



                var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

                var randomFiveNumbers = _.sample(x, 5);





                share|improve this answer




















                • 1





                  This produces only 1 element for me, not 5.

                  – Snowman
                  Aug 21 '16 at 16:02











                • From underscore's documentation: "Produce a random sample from the list. Pass a number to return n random elements from the list. Otherwise a single random item will be returned." - Did you pass in the second parameter?

                  – alengel
                  Aug 22 '16 at 11:18












                • lodash has a _.sampleSize that works as described above: lodash.com/docs/4.17.4#sampleSize

                  – tonyg
                  Dec 20 '17 at 17:27















                11














                A little late to the party but this could be solved with underscore's new sample method (underscore 1.5.2 - Sept 2013):



                var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

                var randomFiveNumbers = _.sample(x, 5);





                share|improve this answer




















                • 1





                  This produces only 1 element for me, not 5.

                  – Snowman
                  Aug 21 '16 at 16:02











                • From underscore's documentation: "Produce a random sample from the list. Pass a number to return n random elements from the list. Otherwise a single random item will be returned." - Did you pass in the second parameter?

                  – alengel
                  Aug 22 '16 at 11:18












                • lodash has a _.sampleSize that works as described above: lodash.com/docs/4.17.4#sampleSize

                  – tonyg
                  Dec 20 '17 at 17:27













                11












                11








                11







                A little late to the party but this could be solved with underscore's new sample method (underscore 1.5.2 - Sept 2013):



                var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

                var randomFiveNumbers = _.sample(x, 5);





                share|improve this answer















                A little late to the party but this could be solved with underscore's new sample method (underscore 1.5.2 - Sept 2013):



                var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

                var randomFiveNumbers = _.sample(x, 5);






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Jun 27 '14 at 6:22









                ntalbs

                17.9k74670




                17.9k74670










                answered Oct 28 '13 at 9:27









                alengelalengel

                1,09611425




                1,09611425







                • 1





                  This produces only 1 element for me, not 5.

                  – Snowman
                  Aug 21 '16 at 16:02











                • From underscore's documentation: "Produce a random sample from the list. Pass a number to return n random elements from the list. Otherwise a single random item will be returned." - Did you pass in the second parameter?

                  – alengel
                  Aug 22 '16 at 11:18












                • lodash has a _.sampleSize that works as described above: lodash.com/docs/4.17.4#sampleSize

                  – tonyg
                  Dec 20 '17 at 17:27












                • 1





                  This produces only 1 element for me, not 5.

                  – Snowman
                  Aug 21 '16 at 16:02











                • From underscore's documentation: "Produce a random sample from the list. Pass a number to return n random elements from the list. Otherwise a single random item will be returned." - Did you pass in the second parameter?

                  – alengel
                  Aug 22 '16 at 11:18












                • lodash has a _.sampleSize that works as described above: lodash.com/docs/4.17.4#sampleSize

                  – tonyg
                  Dec 20 '17 at 17:27







                1




                1





                This produces only 1 element for me, not 5.

                – Snowman
                Aug 21 '16 at 16:02





                This produces only 1 element for me, not 5.

                – Snowman
                Aug 21 '16 at 16:02













                From underscore's documentation: "Produce a random sample from the list. Pass a number to return n random elements from the list. Otherwise a single random item will be returned." - Did you pass in the second parameter?

                – alengel
                Aug 22 '16 at 11:18






                From underscore's documentation: "Produce a random sample from the list. Pass a number to return n random elements from the list. Otherwise a single random item will be returned." - Did you pass in the second parameter?

                – alengel
                Aug 22 '16 at 11:18














                lodash has a _.sampleSize that works as described above: lodash.com/docs/4.17.4#sampleSize

                – tonyg
                Dec 20 '17 at 17:27





                lodash has a _.sampleSize that works as described above: lodash.com/docs/4.17.4#sampleSize

                – tonyg
                Dec 20 '17 at 17:27











                6














                Or... if you use underscore.js...



                _und = require('underscore');

                ...

                function sample(a, n)
                return _und.take(_und.shuffle(a), n);



                Simple enough.






                share|improve this answer





























                  6














                  Or... if you use underscore.js...



                  _und = require('underscore');

                  ...

                  function sample(a, n)
                  return _und.take(_und.shuffle(a), n);



                  Simple enough.






                  share|improve this answer



























                    6












                    6








                    6







                    Or... if you use underscore.js...



                    _und = require('underscore');

                    ...

                    function sample(a, n)
                    return _und.take(_und.shuffle(a), n);



                    Simple enough.






                    share|improve this answer















                    Or... if you use underscore.js...



                    _und = require('underscore');

                    ...

                    function sample(a, n)
                    return _und.take(_und.shuffle(a), n);



                    Simple enough.







                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Oct 26 '12 at 16:47









                    Chris Kimpton

                    4,35364068




                    4,35364068










                    answered Aug 13 '12 at 14:26









                    ntalbsntalbs

                    17.9k74670




                    17.9k74670





















                        2














                        You can remove the elements from a copy of the array as you select them. Performance is probably not ideal, but it might be OK for what you need:



                        function getRandom(arr, size) 
                        var copy = arr.slice(0), rand = ;
                        for (var i = 0; i < size && i < copy.length; i++)
                        var index = Math.floor(Math.random() * copy.length);
                        rand.push(copy.splice(index, 1)[0]);

                        return rand;






                        share|improve this answer



























                          2














                          You can remove the elements from a copy of the array as you select them. Performance is probably not ideal, but it might be OK for what you need:



                          function getRandom(arr, size) 
                          var copy = arr.slice(0), rand = ;
                          for (var i = 0; i < size && i < copy.length; i++)
                          var index = Math.floor(Math.random() * copy.length);
                          rand.push(copy.splice(index, 1)[0]);

                          return rand;






                          share|improve this answer

























                            2












                            2








                            2







                            You can remove the elements from a copy of the array as you select them. Performance is probably not ideal, but it might be OK for what you need:



                            function getRandom(arr, size) 
                            var copy = arr.slice(0), rand = ;
                            for (var i = 0; i < size && i < copy.length; i++)
                            var index = Math.floor(Math.random() * copy.length);
                            rand.push(copy.splice(index, 1)[0]);

                            return rand;






                            share|improve this answer













                            You can remove the elements from a copy of the array as you select them. Performance is probably not ideal, but it might be OK for what you need:



                            function getRandom(arr, size) 
                            var copy = arr.slice(0), rand = ;
                            for (var i = 0; i < size && i < copy.length; i++)
                            var index = Math.floor(Math.random() * copy.length);
                            rand.push(copy.splice(index, 1)[0]);

                            return rand;







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Aug 13 '12 at 13:41









                            mamapitufomamapitufo

                            3,5222018




                            3,5222018





















                                2














                                While I strongly support using the Fisher-Yates Shuffle, as suggested by Tim Down, here's a very short method for achieving a random subset as requested, mathematically correct, including the empty set, and the given set itself.



                                Note solution depends on lodash / underscore:



                                function subset(arr) 
                                return _.sample(arr, _.random(arr.length));






                                share|improve this answer





























                                  2














                                  While I strongly support using the Fisher-Yates Shuffle, as suggested by Tim Down, here's a very short method for achieving a random subset as requested, mathematically correct, including the empty set, and the given set itself.



                                  Note solution depends on lodash / underscore:



                                  function subset(arr) 
                                  return _.sample(arr, _.random(arr.length));






                                  share|improve this answer



























                                    2












                                    2








                                    2







                                    While I strongly support using the Fisher-Yates Shuffle, as suggested by Tim Down, here's a very short method for achieving a random subset as requested, mathematically correct, including the empty set, and the given set itself.



                                    Note solution depends on lodash / underscore:



                                    function subset(arr) 
                                    return _.sample(arr, _.random(arr.length));






                                    share|improve this answer















                                    While I strongly support using the Fisher-Yates Shuffle, as suggested by Tim Down, here's a very short method for achieving a random subset as requested, mathematically correct, including the empty set, and the given set itself.



                                    Note solution depends on lodash / underscore:



                                    function subset(arr) 
                                    return _.sample(arr, _.random(arr.length));







                                    share|improve this answer














                                    share|improve this answer



                                    share|improve this answer








                                    edited May 23 '17 at 11:46









                                    Community

                                    11




                                    11










                                    answered Jan 11 '15 at 12:41









                                    SelfishSelfish

                                    3,62322449




                                    3,62322449





















                                        2














                                        In my opinion, I do not think shuffling the entire deck necessary. You just need to make sure your sample is random not your deck. What you can do, is select the size amount from the front then swap each one in the sampling array with another position in it. So, if you allow replacement you get more and more shuffled.



                                        function getRandom(length) return Math.floor(Math.random()*(length)); 

                                        function getRandomSample(array, size)
                                        var length = array.length;

                                        for(var i = size; i--;)
                                        var index = getRandom(length);
                                        var temp = array[index];
                                        array[index] = array[i];
                                        array[i] = temp;


                                        return array.slice(0, size);



                                        This algorithm is only 2*size steps, if you include the slice method, to select the random sample.




                                        More Random



                                        To make the sample more random, we can randomly select the starting point of the sample. But it is a little more expensive to get the sample.



                                        function getRandomSample(array, size) 
                                        var length = array.length, start = getRandom(length);

                                        for(var i = size; i--;)
                                        var index = (start + i)%length, rindex = getRandom(length);
                                        var temp = array[rindex];
                                        array[rindex] = array[index];
                                        array[index] = temp;

                                        var end = start + size, sample = array.slice(start, end);
                                        if(end > length)
                                        sample = sample.concat(array.slice(0, end - length));
                                        return sample;



                                        What makes this more random is the fact that when you always just shuffling the front items you tend to not get them very often in the sample if the sampling array is large and the sample is small. This would not be a problem if the array was not supposed to always be the same. So, what this method does is change up this position where the shuffled region starts.




                                        No Replacement



                                        To not have to copy the sampling array and not worry about replacement, you can do the following but it does give you 3*size vs the 2*size.



                                        function getRandomSample(array, size) 
                                        var length = array.length, swaps = , i = size, temp;

                                        while(i--)
                                        var rindex = getRandom(length);
                                        temp = array[rindex];
                                        array[rindex] = array[i];
                                        array[i] = temp;
                                        swaps.push( from: i, to: rindex );


                                        var sample = array.slice(0, size);

                                        // Put everything back.
                                        i = size;
                                        while(i--)
                                        var pop = swaps.pop();
                                        temp = array[pop.from];
                                        array[pop.from] = array[pop.to];
                                        array[pop.to] = temp;


                                        return sample;




                                        No Replacement and More Random



                                        To apply the algorithm that gave a little bit more random samples to the no replacement function:



                                        function getRandomSample(array, size) 
                                        var length = array.length, start = getRandom(length),
                                        swaps = , i = size, temp;

                                        while(i--)
                                        var index = (start + i)%length, rindex = getRandom(length);
                                        temp = array[rindex];
                                        array[rindex] = array[index];
                                        array[index] = temp;
                                        swaps.push( from: index, to: rindex );


                                        var end = start + size, sample = array.slice(start, end);
                                        if(end > length)
                                        sample = sample.concat(array.slice(0, end - length));

                                        // Put everything back.
                                        i = size;
                                        while(i--)
                                        var pop = swaps.pop();
                                        temp = array[pop.from];
                                        array[pop.from] = array[pop.to];
                                        array[pop.to] = temp;


                                        return sample;




                                        Faster...



                                        Like all of these post, this uses the Fisher-Yates Shuffle. But, I removed the over head of copying the array.



                                        function getRandomSample(array, size) 
                                        var r, i = array.length, end = i - size, temp, swaps = getRandomSample.swaps;

                                        while (i-- > end)
                                        r = getRandom(i + 1);
                                        temp = array[r];
                                        array[r] = array[i];
                                        array[i] = temp;
                                        swaps.push(i);
                                        swaps.push(r);


                                        var sample = array.slice(end);

                                        while(size--)
                                        i = swaps.pop();
                                        r = swaps.pop();
                                        temp = array[i];
                                        array[i] = array[r];
                                        array[r] = temp;


                                        return sample;

                                        getRandomSample.swaps = ;





                                        share|improve this answer





























                                          2














                                          In my opinion, I do not think shuffling the entire deck necessary. You just need to make sure your sample is random not your deck. What you can do, is select the size amount from the front then swap each one in the sampling array with another position in it. So, if you allow replacement you get more and more shuffled.



                                          function getRandom(length) return Math.floor(Math.random()*(length)); 

                                          function getRandomSample(array, size)
                                          var length = array.length;

                                          for(var i = size; i--;)
                                          var index = getRandom(length);
                                          var temp = array[index];
                                          array[index] = array[i];
                                          array[i] = temp;


                                          return array.slice(0, size);



                                          This algorithm is only 2*size steps, if you include the slice method, to select the random sample.




                                          More Random



                                          To make the sample more random, we can randomly select the starting point of the sample. But it is a little more expensive to get the sample.



                                          function getRandomSample(array, size) 
                                          var length = array.length, start = getRandom(length);

                                          for(var i = size; i--;)
                                          var index = (start + i)%length, rindex = getRandom(length);
                                          var temp = array[rindex];
                                          array[rindex] = array[index];
                                          array[index] = temp;

                                          var end = start + size, sample = array.slice(start, end);
                                          if(end > length)
                                          sample = sample.concat(array.slice(0, end - length));
                                          return sample;



                                          What makes this more random is the fact that when you always just shuffling the front items you tend to not get them very often in the sample if the sampling array is large and the sample is small. This would not be a problem if the array was not supposed to always be the same. So, what this method does is change up this position where the shuffled region starts.




                                          No Replacement



                                          To not have to copy the sampling array and not worry about replacement, you can do the following but it does give you 3*size vs the 2*size.



                                          function getRandomSample(array, size) 
                                          var length = array.length, swaps = , i = size, temp;

                                          while(i--)
                                          var rindex = getRandom(length);
                                          temp = array[rindex];
                                          array[rindex] = array[i];
                                          array[i] = temp;
                                          swaps.push( from: i, to: rindex );


                                          var sample = array.slice(0, size);

                                          // Put everything back.
                                          i = size;
                                          while(i--)
                                          var pop = swaps.pop();
                                          temp = array[pop.from];
                                          array[pop.from] = array[pop.to];
                                          array[pop.to] = temp;


                                          return sample;




                                          No Replacement and More Random



                                          To apply the algorithm that gave a little bit more random samples to the no replacement function:



                                          function getRandomSample(array, size) 
                                          var length = array.length, start = getRandom(length),
                                          swaps = , i = size, temp;

                                          while(i--)
                                          var index = (start + i)%length, rindex = getRandom(length);
                                          temp = array[rindex];
                                          array[rindex] = array[index];
                                          array[index] = temp;
                                          swaps.push( from: index, to: rindex );


                                          var end = start + size, sample = array.slice(start, end);
                                          if(end > length)
                                          sample = sample.concat(array.slice(0, end - length));

                                          // Put everything back.
                                          i = size;
                                          while(i--)
                                          var pop = swaps.pop();
                                          temp = array[pop.from];
                                          array[pop.from] = array[pop.to];
                                          array[pop.to] = temp;


                                          return sample;




                                          Faster...



                                          Like all of these post, this uses the Fisher-Yates Shuffle. But, I removed the over head of copying the array.



                                          function getRandomSample(array, size) 
                                          var r, i = array.length, end = i - size, temp, swaps = getRandomSample.swaps;

                                          while (i-- > end)
                                          r = getRandom(i + 1);
                                          temp = array[r];
                                          array[r] = array[i];
                                          array[i] = temp;
                                          swaps.push(i);
                                          swaps.push(r);


                                          var sample = array.slice(end);

                                          while(size--)
                                          i = swaps.pop();
                                          r = swaps.pop();
                                          temp = array[i];
                                          array[i] = array[r];
                                          array[r] = temp;


                                          return sample;

                                          getRandomSample.swaps = ;





                                          share|improve this answer



























                                            2












                                            2








                                            2







                                            In my opinion, I do not think shuffling the entire deck necessary. You just need to make sure your sample is random not your deck. What you can do, is select the size amount from the front then swap each one in the sampling array with another position in it. So, if you allow replacement you get more and more shuffled.



                                            function getRandom(length) return Math.floor(Math.random()*(length)); 

                                            function getRandomSample(array, size)
                                            var length = array.length;

                                            for(var i = size; i--;)
                                            var index = getRandom(length);
                                            var temp = array[index];
                                            array[index] = array[i];
                                            array[i] = temp;


                                            return array.slice(0, size);



                                            This algorithm is only 2*size steps, if you include the slice method, to select the random sample.




                                            More Random



                                            To make the sample more random, we can randomly select the starting point of the sample. But it is a little more expensive to get the sample.



                                            function getRandomSample(array, size) 
                                            var length = array.length, start = getRandom(length);

                                            for(var i = size; i--;)
                                            var index = (start + i)%length, rindex = getRandom(length);
                                            var temp = array[rindex];
                                            array[rindex] = array[index];
                                            array[index] = temp;

                                            var end = start + size, sample = array.slice(start, end);
                                            if(end > length)
                                            sample = sample.concat(array.slice(0, end - length));
                                            return sample;



                                            What makes this more random is the fact that when you always just shuffling the front items you tend to not get them very often in the sample if the sampling array is large and the sample is small. This would not be a problem if the array was not supposed to always be the same. So, what this method does is change up this position where the shuffled region starts.




                                            No Replacement



                                            To not have to copy the sampling array and not worry about replacement, you can do the following but it does give you 3*size vs the 2*size.



                                            function getRandomSample(array, size) 
                                            var length = array.length, swaps = , i = size, temp;

                                            while(i--)
                                            var rindex = getRandom(length);
                                            temp = array[rindex];
                                            array[rindex] = array[i];
                                            array[i] = temp;
                                            swaps.push( from: i, to: rindex );


                                            var sample = array.slice(0, size);

                                            // Put everything back.
                                            i = size;
                                            while(i--)
                                            var pop = swaps.pop();
                                            temp = array[pop.from];
                                            array[pop.from] = array[pop.to];
                                            array[pop.to] = temp;


                                            return sample;




                                            No Replacement and More Random



                                            To apply the algorithm that gave a little bit more random samples to the no replacement function:



                                            function getRandomSample(array, size) 
                                            var length = array.length, start = getRandom(length),
                                            swaps = , i = size, temp;

                                            while(i--)
                                            var index = (start + i)%length, rindex = getRandom(length);
                                            temp = array[rindex];
                                            array[rindex] = array[index];
                                            array[index] = temp;
                                            swaps.push( from: index, to: rindex );


                                            var end = start + size, sample = array.slice(start, end);
                                            if(end > length)
                                            sample = sample.concat(array.slice(0, end - length));

                                            // Put everything back.
                                            i = size;
                                            while(i--)
                                            var pop = swaps.pop();
                                            temp = array[pop.from];
                                            array[pop.from] = array[pop.to];
                                            array[pop.to] = temp;


                                            return sample;




                                            Faster...



                                            Like all of these post, this uses the Fisher-Yates Shuffle. But, I removed the over head of copying the array.



                                            function getRandomSample(array, size) 
                                            var r, i = array.length, end = i - size, temp, swaps = getRandomSample.swaps;

                                            while (i-- > end)
                                            r = getRandom(i + 1);
                                            temp = array[r];
                                            array[r] = array[i];
                                            array[i] = temp;
                                            swaps.push(i);
                                            swaps.push(r);


                                            var sample = array.slice(end);

                                            while(size--)
                                            i = swaps.pop();
                                            r = swaps.pop();
                                            temp = array[i];
                                            array[i] = array[r];
                                            array[r] = temp;


                                            return sample;

                                            getRandomSample.swaps = ;





                                            share|improve this answer















                                            In my opinion, I do not think shuffling the entire deck necessary. You just need to make sure your sample is random not your deck. What you can do, is select the size amount from the front then swap each one in the sampling array with another position in it. So, if you allow replacement you get more and more shuffled.



                                            function getRandom(length) return Math.floor(Math.random()*(length)); 

                                            function getRandomSample(array, size)
                                            var length = array.length;

                                            for(var i = size; i--;)
                                            var index = getRandom(length);
                                            var temp = array[index];
                                            array[index] = array[i];
                                            array[i] = temp;


                                            return array.slice(0, size);



                                            This algorithm is only 2*size steps, if you include the slice method, to select the random sample.




                                            More Random



                                            To make the sample more random, we can randomly select the starting point of the sample. But it is a little more expensive to get the sample.



                                            function getRandomSample(array, size) 
                                            var length = array.length, start = getRandom(length);

                                            for(var i = size; i--;)
                                            var index = (start + i)%length, rindex = getRandom(length);
                                            var temp = array[rindex];
                                            array[rindex] = array[index];
                                            array[index] = temp;

                                            var end = start + size, sample = array.slice(start, end);
                                            if(end > length)
                                            sample = sample.concat(array.slice(0, end - length));
                                            return sample;



                                            What makes this more random is the fact that when you always just shuffling the front items you tend to not get them very often in the sample if the sampling array is large and the sample is small. This would not be a problem if the array was not supposed to always be the same. So, what this method does is change up this position where the shuffled region starts.




                                            No Replacement



                                            To not have to copy the sampling array and not worry about replacement, you can do the following but it does give you 3*size vs the 2*size.



                                            function getRandomSample(array, size) 
                                            var length = array.length, swaps = , i = size, temp;

                                            while(i--)
                                            var rindex = getRandom(length);
                                            temp = array[rindex];
                                            array[rindex] = array[i];
                                            array[i] = temp;
                                            swaps.push( from: i, to: rindex );


                                            var sample = array.slice(0, size);

                                            // Put everything back.
                                            i = size;
                                            while(i--)
                                            var pop = swaps.pop();
                                            temp = array[pop.from];
                                            array[pop.from] = array[pop.to];
                                            array[pop.to] = temp;


                                            return sample;




                                            No Replacement and More Random



                                            To apply the algorithm that gave a little bit more random samples to the no replacement function:



                                            function getRandomSample(array, size) 
                                            var length = array.length, start = getRandom(length),
                                            swaps = , i = size, temp;

                                            while(i--)
                                            var index = (start + i)%length, rindex = getRandom(length);
                                            temp = array[rindex];
                                            array[rindex] = array[index];
                                            array[index] = temp;
                                            swaps.push( from: index, to: rindex );


                                            var end = start + size, sample = array.slice(start, end);
                                            if(end > length)
                                            sample = sample.concat(array.slice(0, end - length));

                                            // Put everything back.
                                            i = size;
                                            while(i--)
                                            var pop = swaps.pop();
                                            temp = array[pop.from];
                                            array[pop.from] = array[pop.to];
                                            array[pop.to] = temp;


                                            return sample;




                                            Faster...



                                            Like all of these post, this uses the Fisher-Yates Shuffle. But, I removed the over head of copying the array.



                                            function getRandomSample(array, size) 
                                            var r, i = array.length, end = i - size, temp, swaps = getRandomSample.swaps;

                                            while (i-- > end)
                                            r = getRandom(i + 1);
                                            temp = array[r];
                                            array[r] = array[i];
                                            array[i] = temp;
                                            swaps.push(i);
                                            swaps.push(r);


                                            var sample = array.slice(end);

                                            while(size--)
                                            i = swaps.pop();
                                            r = swaps.pop();
                                            temp = array[i];
                                            array[i] = array[r];
                                            array[r] = temp;


                                            return sample;

                                            getRandomSample.swaps = ;






                                            share|improve this answer














                                            share|improve this answer



                                            share|improve this answer








                                            edited Jun 17 '16 at 15:51

























                                            answered Jun 15 '16 at 12:37









                                            tkellehetkellehe

                                            52639




                                            52639





















                                                2














                                                If you're using lodash the API changed in 4.x:



                                                const oneItem = _.sample(arr);
                                                const nItems = _.sampleSize(arr, n);


                                                https://lodash.com/docs#sampleSize






                                                share|improve this answer



























                                                  2














                                                  If you're using lodash the API changed in 4.x:



                                                  const oneItem = _.sample(arr);
                                                  const nItems = _.sampleSize(arr, n);


                                                  https://lodash.com/docs#sampleSize






                                                  share|improve this answer

























                                                    2












                                                    2








                                                    2







                                                    If you're using lodash the API changed in 4.x:



                                                    const oneItem = _.sample(arr);
                                                    const nItems = _.sampleSize(arr, n);


                                                    https://lodash.com/docs#sampleSize






                                                    share|improve this answer













                                                    If you're using lodash the API changed in 4.x:



                                                    const oneItem = _.sample(arr);
                                                    const nItems = _.sampleSize(arr, n);


                                                    https://lodash.com/docs#sampleSize







                                                    share|improve this answer












                                                    share|improve this answer



                                                    share|improve this answer










                                                    answered Aug 27 '16 at 21:02









                                                    chovychovy

                                                    36k32157213




                                                    36k32157213





















                                                        1














                                                        Here is another implementation based on Fisher-Yater Shuffle. But this one is optimized for the case where the sample size is significantly smaller than the array length. This implementation doesn't scan the entire array nor allocates arrays as large as the original array. It uses sparse arrays to reduce memory allocation.



                                                        function getRandomSample(array, count) 
                                                        var indices = ;
                                                        var result = new Array(count);
                                                        for (let i = 0; i < count; i++ )
                                                        let j = Math.floor(Math.random() * (array.length - i) + i);
                                                        result[i] = array[indices[j] === undefined ? j : indices[j]];
                                                        indices[j] = indices[i] === undefined ? i : indices[i];

                                                        return result;






                                                        share|improve this answer























                                                        • I don't how this works, but it does -- and it's much more efficient when count << array.length. To make it completely generic (i.e., when count is equal or greater than array length) I added: ` let val = array[indices[j] === undefined ? j : indices[j]]; if (val === undefined) result.length = i; break; result[i] = val; ` to force result.length <= array.length, otherwise will get bunch of undefineds in the result.

                                                          – Moos
                                                          Oct 31 '18 at 23:15
















                                                        1














                                                        Here is another implementation based on Fisher-Yater Shuffle. But this one is optimized for the case where the sample size is significantly smaller than the array length. This implementation doesn't scan the entire array nor allocates arrays as large as the original array. It uses sparse arrays to reduce memory allocation.



                                                        function getRandomSample(array, count) 
                                                        var indices = ;
                                                        var result = new Array(count);
                                                        for (let i = 0; i < count; i++ )
                                                        let j = Math.floor(Math.random() * (array.length - i) + i);
                                                        result[i] = array[indices[j] === undefined ? j : indices[j]];
                                                        indices[j] = indices[i] === undefined ? i : indices[i];

                                                        return result;






                                                        share|improve this answer























                                                        • I don't how this works, but it does -- and it's much more efficient when count << array.length. To make it completely generic (i.e., when count is equal or greater than array length) I added: ` let val = array[indices[j] === undefined ? j : indices[j]]; if (val === undefined) result.length = i; break; result[i] = val; ` to force result.length <= array.length, otherwise will get bunch of undefineds in the result.

                                                          – Moos
                                                          Oct 31 '18 at 23:15














                                                        1












                                                        1








                                                        1







                                                        Here is another implementation based on Fisher-Yater Shuffle. But this one is optimized for the case where the sample size is significantly smaller than the array length. This implementation doesn't scan the entire array nor allocates arrays as large as the original array. It uses sparse arrays to reduce memory allocation.



                                                        function getRandomSample(array, count) 
                                                        var indices = ;
                                                        var result = new Array(count);
                                                        for (let i = 0; i < count; i++ )
                                                        let j = Math.floor(Math.random() * (array.length - i) + i);
                                                        result[i] = array[indices[j] === undefined ? j : indices[j]];
                                                        indices[j] = indices[i] === undefined ? i : indices[i];

                                                        return result;






                                                        share|improve this answer













                                                        Here is another implementation based on Fisher-Yater Shuffle. But this one is optimized for the case where the sample size is significantly smaller than the array length. This implementation doesn't scan the entire array nor allocates arrays as large as the original array. It uses sparse arrays to reduce memory allocation.



                                                        function getRandomSample(array, count) 
                                                        var indices = ;
                                                        var result = new Array(count);
                                                        for (let i = 0; i < count; i++ )
                                                        let j = Math.floor(Math.random() * (array.length - i) + i);
                                                        result[i] = array[indices[j] === undefined ? j : indices[j]];
                                                        indices[j] = indices[i] === undefined ? i : indices[i];

                                                        return result;







                                                        share|improve this answer












                                                        share|improve this answer



                                                        share|improve this answer










                                                        answered Jun 15 '16 at 11:31









                                                        Jesús LópezJesús López

                                                        4,23411442




                                                        4,23411442












                                                        • I don't how this works, but it does -- and it's much more efficient when count << array.length. To make it completely generic (i.e., when count is equal or greater than array length) I added: ` let val = array[indices[j] === undefined ? j : indices[j]]; if (val === undefined) result.length = i; break; result[i] = val; ` to force result.length <= array.length, otherwise will get bunch of undefineds in the result.

                                                          – Moos
                                                          Oct 31 '18 at 23:15


















                                                        • I don't how this works, but it does -- and it's much more efficient when count << array.length. To make it completely generic (i.e., when count is equal or greater than array length) I added: ` let val = array[indices[j] === undefined ? j : indices[j]]; if (val === undefined) result.length = i; break; result[i] = val; ` to force result.length <= array.length, otherwise will get bunch of undefineds in the result.

                                                          – Moos
                                                          Oct 31 '18 at 23:15

















                                                        I don't how this works, but it does -- and it's much more efficient when count << array.length. To make it completely generic (i.e., when count is equal or greater than array length) I added: ` let val = array[indices[j] === undefined ? j : indices[j]]; if (val === undefined) result.length = i; break; result[i] = val; ` to force result.length <= array.length, otherwise will get bunch of undefineds in the result.

                                                        – Moos
                                                        Oct 31 '18 at 23:15






                                                        I don't how this works, but it does -- and it's much more efficient when count << array.length. To make it completely generic (i.e., when count is equal or greater than array length) I added: ` let val = array[indices[j] === undefined ? j : indices[j]]; if (val === undefined) result.length = i; break; result[i] = val; ` to force result.length <= array.length, otherwise will get bunch of undefineds in the result.

                                                        – Moos
                                                        Oct 31 '18 at 23:15












                                                        1














                                                        You can get a 5 elements sample by this way:



                                                        var sample = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
                                                        .map(a => [a,Math.random()])
                                                        .sort((a,b) => return a[1] < b[1] ? -1 : 1;)
                                                        .slice(0,5)
                                                        .map(a => a[0]);


                                                        You can define it as a function to use in your code:



                                                        var randomSample = function(arr,num) return arr.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).slice(0,num).map(a => a[0]); 


                                                        Or add it to the Array object itself:



                                                        Array.prototype.sample = function(num) return this.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).slice(0,num).map(a => a[0]); ;


                                                        if you want, you can separate the code for to have 2 functionalities (Shuffle and Sample):



                                                        Array.prototype.shuffle = function() return this.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).map(a => a[0]); ;
                                                        Array.prototype.sample = function(num) return this.shuffle().slice(0,num); ;





                                                        share|improve this answer



























                                                          1














                                                          You can get a 5 elements sample by this way:



                                                          var sample = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
                                                          .map(a => [a,Math.random()])
                                                          .sort((a,b) => return a[1] < b[1] ? -1 : 1;)
                                                          .slice(0,5)
                                                          .map(a => a[0]);


                                                          You can define it as a function to use in your code:



                                                          var randomSample = function(arr,num) return arr.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).slice(0,num).map(a => a[0]); 


                                                          Or add it to the Array object itself:



                                                          Array.prototype.sample = function(num) return this.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).slice(0,num).map(a => a[0]); ;


                                                          if you want, you can separate the code for to have 2 functionalities (Shuffle and Sample):



                                                          Array.prototype.shuffle = function() return this.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).map(a => a[0]); ;
                                                          Array.prototype.sample = function(num) return this.shuffle().slice(0,num); ;





                                                          share|improve this answer

























                                                            1












                                                            1








                                                            1







                                                            You can get a 5 elements sample by this way:



                                                            var sample = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
                                                            .map(a => [a,Math.random()])
                                                            .sort((a,b) => return a[1] < b[1] ? -1 : 1;)
                                                            .slice(0,5)
                                                            .map(a => a[0]);


                                                            You can define it as a function to use in your code:



                                                            var randomSample = function(arr,num) return arr.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).slice(0,num).map(a => a[0]); 


                                                            Or add it to the Array object itself:



                                                            Array.prototype.sample = function(num) return this.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).slice(0,num).map(a => a[0]); ;


                                                            if you want, you can separate the code for to have 2 functionalities (Shuffle and Sample):



                                                            Array.prototype.shuffle = function() return this.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).map(a => a[0]); ;
                                                            Array.prototype.sample = function(num) return this.shuffle().slice(0,num); ;





                                                            share|improve this answer













                                                            You can get a 5 elements sample by this way:



                                                            var sample = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
                                                            .map(a => [a,Math.random()])
                                                            .sort((a,b) => return a[1] < b[1] ? -1 : 1;)
                                                            .slice(0,5)
                                                            .map(a => a[0]);


                                                            You can define it as a function to use in your code:



                                                            var randomSample = function(arr,num) return arr.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).slice(0,num).map(a => a[0]); 


                                                            Or add it to the Array object itself:



                                                            Array.prototype.sample = function(num) return this.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).slice(0,num).map(a => a[0]); ;


                                                            if you want, you can separate the code for to have 2 functionalities (Shuffle and Sample):



                                                            Array.prototype.shuffle = function() return this.map(a => [a,Math.random()]).sort((a,b) => return a[1] < b[1] ? -1 : 1;).map(a => a[0]); ;
                                                            Array.prototype.sample = function(num) return this.shuffle().slice(0,num); ;






                                                            share|improve this answer












                                                            share|improve this answer



                                                            share|improve this answer










                                                            answered Sep 8 '18 at 7:12









                                                            Luis MarinLuis Marin

                                                            112




                                                            112





















                                                                0














                                                                Perhaps I am missing something, but it seems there is a solution that does not require the complexity or potential overhead of a shuffle:



                                                                function sample(array,size) 
                                                                const results = ,
                                                                sampled = ;
                                                                while(results.length<size && results.length<array.length)
                                                                const index = Math.trunc(Math.random() * array.length);
                                                                if(!sampled[index])
                                                                results.push(array[index]);
                                                                sampled[index] = true;


                                                                return results;






                                                                share|improve this answer



























                                                                  0














                                                                  Perhaps I am missing something, but it seems there is a solution that does not require the complexity or potential overhead of a shuffle:



                                                                  function sample(array,size) 
                                                                  const results = ,
                                                                  sampled = ;
                                                                  while(results.length<size && results.length<array.length)
                                                                  const index = Math.trunc(Math.random() * array.length);
                                                                  if(!sampled[index])
                                                                  results.push(array[index]);
                                                                  sampled[index] = true;


                                                                  return results;






                                                                  share|improve this answer

























                                                                    0












                                                                    0








                                                                    0







                                                                    Perhaps I am missing something, but it seems there is a solution that does not require the complexity or potential overhead of a shuffle:



                                                                    function sample(array,size) 
                                                                    const results = ,
                                                                    sampled = ;
                                                                    while(results.length<size && results.length<array.length)
                                                                    const index = Math.trunc(Math.random() * array.length);
                                                                    if(!sampled[index])
                                                                    results.push(array[index]);
                                                                    sampled[index] = true;


                                                                    return results;






                                                                    share|improve this answer













                                                                    Perhaps I am missing something, but it seems there is a solution that does not require the complexity or potential overhead of a shuffle:



                                                                    function sample(array,size) 
                                                                    const results = ,
                                                                    sampled = ;
                                                                    while(results.length<size && results.length<array.length)
                                                                    const index = Math.trunc(Math.random() * array.length);
                                                                    if(!sampled[index])
                                                                    results.push(array[index]);
                                                                    sampled[index] = true;


                                                                    return results;







                                                                    share|improve this answer












                                                                    share|improve this answer



                                                                    share|improve this answer










                                                                    answered Jan 12 at 14:32









                                                                    AnyWhichWayAnyWhichWay

                                                                    13227




                                                                    13227



























                                                                        draft saved

                                                                        draft discarded
















































                                                                        Thanks for contributing an answer to Stack Overflow!


                                                                        • Please be sure to answer the question. Provide details and share your research!

                                                                        But avoid


                                                                        • Asking for help, clarification, or responding to other answers.

                                                                        • Making statements based on opinion; back them up with references or personal experience.

                                                                        To learn more, see our tips on writing great answers.




                                                                        draft saved


                                                                        draft discarded














                                                                        StackExchange.ready(
                                                                        function ()
                                                                        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f11935175%2fsampling-a-random-subset-from-an-array%23new-answer', 'question_page');

                                                                        );

                                                                        Post as a guest















                                                                        Required, but never shown





















































                                                                        Required, but never shown














                                                                        Required, but never shown












                                                                        Required, but never shown







                                                                        Required, but never shown

































                                                                        Required, but never shown














                                                                        Required, but never shown












                                                                        Required, but never shown







                                                                        Required, but never shown







                                                                        Popular posts from this blog

                                                                        Top Tejano songwriter Luis Silva dead of heart attack at 64

                                                                        政党

                                                                        天津地下鉄3号線