How do I shuffle the characters in a string in JavaScript?

Viewed 65713

In particular, I want to make sure to avoid the mistake made in Microsoft's Browser Choice shuffle code. That is, I want to make sure that each letter has an equal probability of ending up in each possible position.

e.g. Given "ABCDEFG", return something like "GEFBDCA".

14 Answers

Shortest One Liner:

const shuffle = str => [...str].sort(()=>Math.random()-.5).join('');

Does not guarantee statistically equal distribution but usable in most cases for me.

const shuffle = str => [...str].sort(()=>Math.random()-.5).join('');
document.write(shuffle("The quick brown fox jumps over the lazy dog"));

Using Fisher-Yates shuffle algorithm and ES6:

// Original string
let string = 'ABCDEFG';

// Create a copy of the original string to be randomized ['A', 'B', ... , 'G']
let shuffle = [...string];

// Defining function returning random value from i to N
const getRandomValue = (i, N) => Math.floor(Math.random() * (N - i) + i);

// Shuffle a pair of two elements at random position j (Fisher-Yates)
shuffle.forEach( (elem, i, arr, j = getRandomValue(i, arr.length)) => [arr[i], arr[j]] = [arr[j], arr[i]] );

// Transforming array to string
shuffle = shuffle.join('');

console.log(shuffle);
// 'GBEADFC'

Single line solution and limited output length...

var rnd = "ABCDEF23456789".split('').sort(function(){return 0.5-Math.random()}).join('').substring(0,6);

A different take on scrambling a word. All other answers with enough iterations will return the word unscrambled, mine does not.

var scramble = word => {

    var unique = {};
    var newWord = "";
    var wordLength = word.length;

    word = word.toLowerCase(); //Because why would we want to make it easy for them?

    while(wordLength != newWord.length) {

        var random = ~~(Math.random() * wordLength);

        if(

          unique[random]
          ||
          random == newWord.length && random != (wordLength - 1) //Don't put the character at the same index it was, nore get stuck in a infinite loop.

        ) continue; //This is like return but for while loops to start over.

        unique[random] = true;
        newWord += word[random];

    };

    return newWord;

};

scramble("God"); //dgo, gdo, ogd

Yet another Fisher-Yates implementation:

const str = 'ABCDEFG',

      shuffle = str => 
        [...str]
          .reduceRight((res,_,__,arr) => (
            res.push(...arr.splice(0|Math.random()*arr.length,1)),
            res) ,[])
          .join('')

console.log(shuffle(str))
.as-console-wrapper{min-height:100%;}

Just for the sake of completeness even though this may not be exactly what the OP was after as that particular question has already been answered.

Here's one that shuffles words.

Here's the regex explanation for that: https://regex101.com/r/aFcEtk/1

And it also has some funny outcomes.

// Shuffles words
// var str = "1 2 3 4 5 6 7 8 9 10";
var str = "the quick brown fox jumps over the lazy dog A.S.A.P. That's right, this happened.";
var every_word_im_shuffling = str.split(/\s\b(?!\s)/).sort(function(){return 0.5-Math.random()}).join(' ');
console.log(every_word_im_shuffling);

Rando.js uses a cryptographically secure version of the Fisher-Yates shuffle and is pretty short and readable.

console.log(randoSequence("This string will be shuffled.").join(""));
<script src="https://randojs.com/2.0.0.js"></script>

You can't shuffe a string in place but you can use this:

String.prototype.shuffle = function() {
    var len = this.length;
    var d = len;
    var s = this.split("");
    var array = [];
    var k, i;
    for (i = 0; i < d; i++) {
        k = Math.floor(Math.random() * len);
        array.push(s[k]);
        s.splice(k, 1);
        len = s.length;
    }
    for (i = 0; i < d; i++) {
        this[i] = array[i];
    }
    return array.join("");
}

var s = "ABCDE";
s = s.shuffle();
console.log(s);

String.prototype.shuffle = function(){
  return this.split('').sort(function(a,b){
    return (7 - (Math.random()+'')[5]);
  }).join('');
};
Related