552

I want to shuffle an array of elements in JavaScript like these:

[0, 3, 3] -> [3, 0, 3]
[9, 3, 6, 0, 6] -> [0, 3, 6, 9, 6]
[3, 3, 6, 0, 6] -> [0, 3, 6, 3, 6]
Community
  • 1
  • 1
Anshul
  • 7,914
  • 12
  • 42
  • 65
  • 10
    This has been answered a number of times on stackoverflow. Check http://stackoverflow.com/questions/2450954/how-to-randomize-a-javascript-array here's another: http://stackoverflow.com/questions/5086262/javascript-array-shuffle-with-padding – joekarl Jun 08 '11 at 04:57
  • 1
    A good resource for [JavaScript Shuffle, Deal, Draw](http://www.merlyn.demon.co.uk/js-shufl.htm#FnB) and other date and mathematic stuff. – RobG Jun 08 '11 at 05:18
  • 3
    What about a one-liner? The returned array is shuffled. arr1.reduce((a,v)=>a.splice(Math.floor(Math.random() * a.length), 0, v) && a, []) – brunettdan Oct 16 '17 at 19:52
  • you can use sort() with pseudo random function, while if you have non numeric members, you can simply add numeric field. – Vitali Pom Nov 03 '17 at 16:25
  • http://stackoverflow.com/a/25984542/1238884 – frumbert Apr 30 '18 at 06:49
  • 1
    @VitaliPom Don't use sort() with random(). Sort does not expect random result and the result may not be uniform. Microsoft's browser ballot was [bugged](https://www.robweir.com/blog/2010/02/microsoft-random-browser-ballot.html) because of this. – Sheepy Apr 08 '19 at 03:51
  • 2
    @brunettdan I wrote this one liner which does not use splice and is much faster: `arr1.reduceRight((p,v,i,a)=>(v=i?~~(Math.random()*(i+1)):i, v-i?[a[v],a[i]]=[a[i],a[v]]:0, a),a)`; Also check out [this function](https://stackoverflow.com/a/25984542/893578). – Sheepy Apr 08 '19 at 04:01
  • @Sheepy thanks! Didn't know that. Took me a while to understand what I wrote what I answered on that :) Thanks for clarifying your answer. – Vitali Pom Apr 11 '19 at 18:16

2 Answers2

1096

Use the modern version of the Fisher–Yates shuffle algorithm:

/**
 * Shuffles array in place.
 * @param {Array} a items An array containing the items.
 */
function shuffle(a) {
    var j, x, i;
    for (i = a.length - 1; i > 0; i--) {
        j = Math.floor(Math.random() * (i + 1));
        x = a[i];
        a[i] = a[j];
        a[j] = x;
    }
    return a;
}

ES2015 (ES6) version

/**
 * Shuffles array in place. ES6 version
 * @param {Array} a items An array containing the items.
 */
function shuffle(a) {
    for (let i = a.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [a[i], a[j]] = [a[j], a[i]];
    }
    return a;
}

Note however, that swapping variables with destructuring assignment causes significant performance loss, as of October 2017.

Use

var myArray = ['1','2','3','4','5','6','7','8','9'];
shuffle(myArray);

Implementing prototype

Using Object.defineProperty (method taken from this SO answer) we can also implement this function as a prototype method for arrays, without having it show up in loops such as for (i in arr). The following will allow you to call arr.shuffle() to shuffle the array arr:

Object.defineProperty(Array.prototype, 'shuffle', {
    value: function() {
        for (let i = this.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [this[i], this[j]] = [this[j], this[i]];
        }
        return this;
    }
});
Joeytje50
  • 18,636
  • 15
  • 63
  • 95
Jeff
  • 12,147
  • 10
  • 51
  • 87
  • 24
    This method (as well as the one below) both modify the original array. That's no big deal, but the example of how to call it is a bit weird. – Michael Jul 11 '14 at 12:52
  • 2
    @Michael +1 for pointing out that reassignment is unnecessary. In fact it's misleading, and probably should have been the FIRST thing pointed out in this comment thread. – ryan-cook Jan 31 '15 at 15:24
  • 3
    I find the ES6 swap to be slower (once I got it to work. You have to have a semicolon before a [--all the more reason to just always use them.). – trlkly Mar 29 '17 at 09:31
  • 1
    @trlkly: ES2015 variant will be slower due to the use of destructuring assignment. Hopefully engines will optimize it soon. – Przemek Oct 12 '17 at 11:11
  • 1
    Why isn't there a test of `i !== j`? That should save a bit of time – Fran Feb 25 '18 at 14:27
  • Should iterate over the array from 0 to length-1, otherwise plenty of cache-misses! – santamanno Apr 09 '18 at 08:11
  • Why `return a;`? Is it simply to allow for things like `shuffle(a).slice(...)`? – aioobe May 03 '18 at 21:18
  • What is the purpose of `(i + 1)` in `const j = Math.floor(Math.random() * (i + 1));`? Wouldn't `Math.floor(Math.random() * i));` be better? – XMB5 Dec 23 '18 at 00:36
  • Is it still true that the destructuring assignment incurs significant performance loss? – julien_c Dec 27 '18 at 17:24
  • 1
    In Node (11), this doesn't seem to be the case. In browsers, it's only marginally different, see https://jsperf.com/shuffle-using-destructuring-assignment – julien_c Dec 27 '18 at 17:39
  • @Regnoult `j` is random, so no, you can't do that. However the loop could start at `i = a.length / 2 + 1`. You don't have to shuffle the same items twice, indeed. – Alexis Wilke Dec 28 '18 at 08:29
  • @aioobe yes for that purpose. @AlexisWilke if `i === j`, example `i=j=2` then the code does `[a[2], a[2]] = [a[2], a[2]]`. No point. And no, you need to iterate on all elements. Think about the case random returns always `1`. Your change wouldn't shuffle half of the array. – Fran Jan 16 '19 at 18:03
  • Sort 1, 10, 100 ? Why is this accepted ? – filipe May 31 '19 at 13:05
  • It doesn't seem semantically correct to use *const* for a variable that changes its value on every iteration of the loop. It's also more to type than *let* or *var*. ;-) – RobG Jun 14 '19 at 20:36
  • 3
    @RobG const is a perfect choice because it's block-scoped, unlike var, and it's redeclared after each interation. let would also work, but since j doesn't change it's value inside for block const is better choice – pauk960 Jul 12 '19 at 13:18
  • @pauk960—regarding "*it's redeclared after each interation*", they aren't. Declarations of all types are only processed once on entering an execution context, see [*ECMA-262 §13.2.14*](http://ecma-international.org/ecma-262/10.0/#sec-blockdeclarationinstantiation). – RobG Jul 14 '19 at 20:43
  • @RobG Winky aside, 2 extra chars is a small price to pay for preventing inadvertent reassignments. `const` communicates intent clearly: "I declare that this variable will never be reassigned". That simplifies cognitive load for reading the code and adds expressiveness -- when you _do_ use `let` (on rare occasions), it communicates "this variable _will_ be reassigned". `const` by default generally makes for better-quality code, smaller functions, fewer pieces of mutable state to keep track of than `let`-heavy, reassignment-happy code. – ggorlen Jul 25 '21 at 21:25
  • @ggorlen—not really the right place for this discussion, however I'll reply once. The concept of constants has been around as long as mathematics and means an invariant value. In the absence of a specific keyword, they were identified using all upper case letters (e.g. Math.PI). Semantically, that is how constants remain. Using *const* for variables such as objects that are then assigned new properties and values is not semantically consistent, e.g. `const obj = {}; obj.prop = 'value';`. *let*, on the other hand, is semantically consistent. – RobG Jul 26 '21 at 00:05
490

You could use the Fisher-Yates Shuffle (code adapted from this site):

function shuffle(array) {
    let counter = array.length;

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

        // Decrease counter by 1
        counter--;

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

    return array;
}
Flexo
  • 87,323
  • 22
  • 191
  • 272
Blender
  • 289,723
  • 53
  • 439
  • 496
  • Care to elaborate on what Closure Compiler does to speed this up? – undefined Jun 27 '13 at 00:25
  • @BrianMortenson: Pass it through and look at the resulting code. It replaces the `while` loop with a `for` loop and turns `counter > 0` into `0 < counter`. I'm not entirely sure what parts speed it up, but the end result is a tiny bit faster than the original. – Blender Jun 27 '13 at 00:28
  • 3
    That first answer seems to have a bug. About once in every 15 runs I get an extra `undefined` column. http://jsfiddle.net/tomasswood/z8zm7/ – Thomas Wood Sep 28 '13 at 00:25
  • @ThomasWood: Yep, you're right. alltom's suggestion wasn't correct. – Blender Sep 28 '13 at 01:12
  • The `~~` is even better than `| 0`: `index = ~~(Math.random() * counter)` – rvighne Oct 11 '13 at 22:51
  • @rvighne: I tested both and `| 0` was marginally faster. – Blender Oct 11 '13 at 23:50
  • there is still a bug in this answer! try: shuffle([1,2,3]); the value "1" is never on position 0 in the returned array – Richard Jan 08 '14 at 14:04
  • 2
    Why you just don't use random + Array.prototype.sort? It's easier and less code than both answers. – volter9 Aug 21 '14 at 16:11
  • 5
    @Volter9: Because the distribution isn't going to be uniform. – Blender Aug 21 '14 at 16:36
  • 30
    really interesting post by jeff atwood about this algorithm. http://blog.codinghorror.com/the-danger-of-naivete/ I wanted to know why it is implemented the way it is – JonnyRaa Oct 29 '14 at 16:49
  • Would be better if some of the `let`s were replaced with `const` as they aren't modified at all, except for `counter`. – Epic Speedy Jul 26 '20 at 22:16
  • 1
    be aware this changes the initial array, its not a functional approach. Just leaving this here for anyone that is blindely copying this (as I did lol). – dcts Jun 10 '21 at 07:14