555

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]
8
  • 10
    This has been answered a number of times on stackoverflow. Check stackoverflow.com/questions/2450954/… here's another: stackoverflow.com/questions/5086262/… Commented Jun 8, 2011 at 4:57
  • 1
    A good resource for JavaScript Shuffle, Deal, Draw and other date and mathematic stuff. Commented Jun 8, 2011 at 5: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, []) Commented Oct 16, 2017 at 19:52
  • 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 because of this. Commented Apr 8, 2019 at 3: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. Commented Apr 8, 2019 at 4:01

2 Answers 2

1098

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;
    }
});
Sign up to request clarification or add additional context in comments.

18 Comments

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 +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.
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: ES2015 variant will be slower due to the use of destructuring assignment. Hopefully engines will optimize it soon.
@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
|
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;
}

12 Comments

That first answer seems to have a bug. About once in every 15 runs I get an extra undefined column. jsfiddle.net/tomasswood/z8zm7
Why you just don't use random + Array.prototype.sort? It's easier and less code than both answers.
@Volter9: Because the distribution isn't going to be uniform.
really interesting post by jeff atwood about this algorithm. blog.codinghorror.com/the-danger-of-naivete I wanted to know why it is implemented the way it is
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).
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.