ChatGPT解决这个技术问题 Extra ChatGPT

How to randomize (shuffle) a JavaScript array?

I have an array like this:

var arr1 = ["a", "b", "c", "d"];

How can I randomize / shuffle it?

Just throwing this here that you can visualize how random a shuffle function actually is with this visualizer Mike Bostock made: bost.ocks.org/mike/shuffle/compare.html
@Blazemonger jsPref is dead. Can you just post here which is the fastest?
How about this? arr1.sort(() => (Math.random() > .5) ? 1 : -1);
a short answer would be a.sort(() => Math.random() - 0.5)
@TheVee see few lines above, on the same spec: "The sort order is implementation-defined if ...If comparefn is not undefined and is not a consistent comparison function for the elements of items"

2
21 revs, 17 users 36%

The de-facto unbiased shuffle algorithm is the Fisher-Yates (aka Knuth) Shuffle.

You can see a great visualization here (and the original post linked to this)

function shuffle(array) { let currentIndex = array.length, randomIndex; // While there remain elements to shuffle. while (currentIndex != 0) { // Pick a remaining element. randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; // And swap it with the current element. [array[currentIndex], array[randomIndex]] = [ array[randomIndex], array[currentIndex]]; } return array; } // Used like so var arr = [2, 11, 37, 42]; shuffle(arr); console.log(arr);

Some more info about the algorithm used.


The above answer skips element 0, the condition should be i-- not --i. Also, the test if (i==0)... is superfluous since if i == 0 the while loop will never be entered. The call to Math.floor can be done faster using ...| 0. Either tempi or tempj can be removed and the value be directly assigned to myArray[i] or j as appropriate.
@RobG the implementation above is functionally correct. In the Fisher-Yates algorithm, the loop isn't meant to run for the first element in the array. Check out wikipedia where there are other implementations that also skip the first element. Also check out this article which talks about why it is important for the loop not to run for the first element.
Be sure to transpile if you're going to do destructuring assignments in a busy loop -- allocating objects is expensive.
@ggorlen What do you mean by transpiling in this context? Can you give us an example or further explanation?
I'm a bit surprised that this is the top answer. There are actually a lot of things wrong... Improper scoping, neglecting to simply use a for loop, incorrectly using != with !==, the infinite loop if passed an empty array, and the modification and return of a parameter.
a
ashleedawg

Here's a JavaScript implementation of the Durstenfeld shuffle, an optimized version of Fisher-Yates:

/* Randomize array in-place using Durstenfeld shuffle algorithm */
function shuffleArray(array) {
    for (var i = array.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

It picks a random element for each original array element, and excludes it from the next draw, like picking randomly from a deck of cards.

This clever exclusion swaps the picked element with the current one, then picks the next random element from the remainder, looping backwards for optimal efficiency, ensuring the random pick is simplified (it can always start at 0), and thereby skipping the final element.

Algorithm runtime is O(n). Note that the shuffle is done in-place so if you don't want to modify the original array, first make a copy of it with .slice(0).

EDIT: Updating to ES6 / ECMAScript 2015

The new ES6 allows us to assign two variables at once. This is especially handy when we want to swap the values of two variables, as we can do it in one line of code. Here is a shorter form of the same function, using this feature.

function shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
}

The implementation in this answer favors the lower end of the array. Found out the hard way. Math.random() should not be multiplied with the loop counter + 1, but with array.lengt()`. See Generating random whole numbers in JavaScript in a specific range? for a very comprehensive explanation.
@MarjanVenema Not sure if you're still watching this space, but this answer is correct, and the change you're suggesting actually introduces bias. See blog.codinghorror.com/the-danger-of-naivete for a nice writeup of this mistake.
repeating user94559's comment with references en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle The element to be swapped (j) should be between 0 and the current array index (i)
Here's the same function, but compressed: function shuffle(a){for(var j,i=a.length-1;i>0;i--){j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]]}}
Did you forget to add return array; ?
s
superluminary

You can do it easily with map and sort:

let unshuffled = ['hello', 'a', 't', 'q', 1, 2, 3, {cats: true}]

let shuffled = unshuffled
  .map(value => ({ value, sort: Math.random() }))
  .sort((a, b) => a.sort - b.sort)
  .map(({ value }) => value)

We put each element in the array in an object, and give it a random sort key We sort using the random key We unmap to get the original objects

You can shuffle polymorphic arrays, and the sort is as random as Math.random, which is good enough for most purposes.

Since the elements are sorted against consistent keys that are not regenerated each iteration, and each comparison pulls from the same distribution, any non-randomness in the distribution of Math.random is canceled out.

Speed

Time complexity is O(N log N), same as quick sort. Space complexity is O(N). This is not as efficient as a Fischer Yates shuffle but, in my opinion, the code is significantly shorter and more functional. If you have a large array you should certainly use Fischer Yates. If you have a small array with a few hundred items, you might do this.


Very nice. This is the Schwartzian transform in js.
This is the best answer here (for short arrays) for a number of reasons. to me, it's really useful because I'm using react in 2021 which works best with a functional approach like this.
Think about the compexity again if you have to map 2 times it goes over the elements N two times already and that is not considering the quick sort complexity of JS's .sort algorithm
@IljaKO 2N is still O(N), which is less than the time complexity of O(N log N)
It is all in the details though. I would not see his approach as O(N log N) and would prefer another approach which is truly O(N log N)
R
Rick

Warning! The use of this algorithm is not recommended, because it is inefficient and strongly biased; see comments. It is being left here for future reference, because the idea is not that rare.

[1,2,3,4,5,6].sort( () => .5 - Math.random() );

This https://javascript.info/array-methods#shuffle-an-array tutorial explains the differences straightforwardly.


Downvoting as this isn't really that random. I don't know why it has so many upvotes. Do not use this method. It looks pretty, but isn't completely correct. Here are results after 10,000 iterations on how many times each number in your array hits index [0] (I can give the other results too): 1 = 29.19%, 2 = 29.53%, 3 = 20.06%, 4 = 11.91%, 5 = 5.99%, 6 = 3.32%
It's fine if you need to randomize relatively small array and not dealing with cryptographic things. I totally agree that if you need more randomness you need to use more complex solution.
The problem is that it's not deterministic, which will give wrong results (if 1 > 2 and 2 > 3, it should be given that 1 > 3, but this will not guarantee that. This will confuse the sort, and give the result commented by @radtad).
L
Lukas Liesis

One could (but should NOT) use it as a protoype from Array:

From ChristopheD:

Array.prototype.shuffle = function() {
  var i = this.length, j, temp;
  if ( i == 0 ) return this;
  while ( --i ) {
     j = Math.floor( Math.random() * ( i + 1 ) );
     temp = this[i];
     this[i] = this[j];
     this[j] = temp;
  }
  return this;
}

Don't touch prototype unless you actually need to shuffle ALL or most of your arrays throughout the program and you are writing this program under the rock where no one will find it. I see this answer is decade old, maybe happen before all the movement of "people, stop extending prototype, it's bad". stackoverflow.com/questions/14034180/…
in the while loop when you get to i being 0 it turns false, therefore ignoring the first element in the list while only shuffling the rest ... so the first element never gets shuffled ... +1 on extending prototype, Makes code more readable in my case.
h
hexacyanide

Use the underscore.js library. The method _.shuffle() is nice for this case. Here is an example with the method:

var _ = require("underscore");

var arr = [1,2,3,4,5,6];
// Testing _.shuffle
var testShuffle = function () {
  var indexOne = 0;
    var stObj = {
      '0': 0,
      '1': 1,
      '2': 2,
      '3': 3,
      '4': 4,
      '5': 5
    };
    for (var i = 0; i < 1000; i++) {
      arr = _.shuffle(arr);
      indexOne = _.indexOf(arr, 1);
      stObj[indexOne] ++;
    }
    console.log(stObj);
};
testShuffle();

C
Community

NEW!

Shorter & probably *faster Fisher-Yates shuffle algorithm

it uses while--- bitwise to floor (numbers up to 10 decimal digits (32bit)) removed unecessary closures & other stuff

function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
 c=a.length;while(c)b=Math.random()*(--c+1)|0,d=a[c],a[c]=a[b],a[b]=d
}

script size (with fy as function name): 90bytes

DEMO http://jsfiddle.net/vvpoma8w/

*faster probably on all browsers except chrome.

If you have any questions just ask.

EDIT

yes it is faster

PERFORMANCE: http://jsperf.com/fyshuffle

using the top voted functions.

EDIT There was a calculation in excess (don't need --c+1) and noone noticed

shorter(4bytes)&faster(test it!).

function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
 c=a.length;while(c)b=Math.random()*c--|0,d=a[c],a[c]=a[b],a[b]=d
}

Caching somewhere else var rnd=Math.random and then use rnd() would also increase slightly the performance on big arrays.

http://jsfiddle.net/vvpoma8w/2/

Readable version (use the original version. this is slower, vars are useless, like the closures & ";", the code itself is also shorter ... maybe read this How to 'minify' Javascript code , btw you are not able to compress the following code in a javascript minifiers like the above one.)

function fisherYates( array ){
 var count = array.length,
     randomnumber,
     temp;
 while( count ){
  randomnumber = Math.random() * count-- | 0;
  temp = array[count];
  array[count] = array[randomnumber];
  array[randomnumber] = temp
 }
}

check out the performance ... 2x faster on most browsers... but needs more jsperf testers...
js is a language that accepts many shortcuts and different ways to write it.. while there are many slow readable functions in here i just like to show how it could be done in a more performant way, also saving some bytes... bitwise and shorthand is really underestimated here and the web is full of buggy and slow code.
Not a slam dunk perf increase. Swapping the fy and shuffle prototype, I get fy consistently at the bottom in Chrome 37 on OS X 10.9.5 (81% slower ~20k ops compared to ~100k) and Safari 7.1 it's up to ~8% slower. YMMV, but it's not always faster. jsperf.com/fyshuffle/3
check stats again... i already wrote chrome is slower beacuse they optimized Math, on all other the bitwise floor and while is faster. check IE, firefox but also mobile devices.Would be also nice to see opera...
mobile safari 1409(fy) vs (shuffle)1253 ,ie 31850(fy) vs (shuffle)13405
n
ns16

Shuffle Array In place

function shuffleArr (array){
    for (var i = array.length - 1; i > 0; i--) {
        var rand = Math.floor(Math.random() * (i + 1));
        [array[i], array[rand]] = [array[rand], array[i]]
    }
}

ES6 Pure, Iterative

const getShuffledArr = arr => {
    const newArr = arr.slice()
    for (let i = newArr.length - 1; i > 0; i--) {
        const rand = Math.floor(Math.random() * (i + 1));
        [newArr[i], newArr[rand]] = [newArr[rand], newArr[i]];
    }
    return newArr
};

Reliability and Performance Test

Some solutions on this page aren't reliable (they only partially randomise the array). Other solutions are significantly less efficient. With testShuffleArrayFun (see below) we can test array shuffling functions for reliability and performance.

function testShuffleArrayFun(getShuffledArrayFun){
    const arr = [0,1,2,3,4,5,6,7,8,9]

    var countArr = arr.map(el=>{
        return arr.map(
            el=> 0
        )
    }) //   For each possible position in the shuffledArr and for 
       //   each possible value, we'll create a counter. 
    const t0 = performance.now()
    const n = 1000000
    for (var i=0 ; i<n ; i++){
        //   We'll call getShuffledArrayFun n times. 
        //   And for each iteration, we'll increment the counter. 
        var shuffledArr = getShuffledArrayFun(arr)
        shuffledArr.forEach(
            (value,key)=>{countArr[key][value]++}
        )
    }
    const t1 = performance.now()
    console.log(`Count Values in position`)
    console.table(countArr)

    const frequencyArr = countArr.map( positionArr => (
        positionArr.map(  
            count => count/n
        )
    )) 

    console.log("Frequency of value in position")
    console.table(frequencyArr)
    console.log(`total time: ${t1-t0}`)
}

Other Solutions

Other solutions just for fun.

ES6 Pure, Recursive

const getShuffledArr = arr => {
    if (arr.length === 1) {return arr};
    const rand = Math.floor(Math.random() * arr.length);
    return [arr[rand], ...getShuffledArr(arr.filter((_, i) => i != rand))];
};

ES6 Pure using array.map

function getShuffledArr (arr){
    return [...arr].map( (_, i, arrCopy) => {
        var rand = i + ( Math.floor( Math.random() * (arrCopy.length - i) ) );
        [arrCopy[rand], arrCopy[i]] = [arrCopy[i], arrCopy[rand]]
        return arrCopy[i]
    })
}

ES6 Pure using array.reduce

function getShuffledArr (arr){
    return arr.reduce( 
        (newArr, _, i) => {
            var rand = i + ( Math.floor( Math.random() * (newArr.length - i) ) );
            [newArr[rand], newArr[i]] = [newArr[i], newArr[rand]]
            return newArr
        }, [...arr]
    )
}

So, where is the ES6(ES2015) ? [array[i], array[rand]]=[array[rand], array[i]] ? Maybe you can outline how that works. Why do you choose to iterate downwards?
@sheriffderek Yes, the ES6 feature I'm using is the assignment of two vars at once, which allows us to swap two vars in one line of code.
Credit to @sheriffderek who suggested the ascending Algorithm. The ascending algorithm could be proved in induction.
B
Ben Carp

Edit: This answer is incorrect

See comments and https://stackoverflow.com/a/18650169/28234. It is being left here for reference because the idea isn't rare.

A very simple way for small arrays is simply this:

const someArray = [1, 2, 3, 4, 5];

someArray.sort(() => Math.random() - 0.5);

It's probably not very efficient, but for small arrays this works just fine. Here's an example so you can see how random (or not) it is, and whether it fits your usecase or not.

const resultsEl = document.querySelector('#results'); const buttonEl = document.querySelector('#trigger'); const generateArrayAndRandomize = () => { const someArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; someArray.sort(() => Math.random() - 0.5); return someArray; }; const renderResultsToDom = (results, el) => { el.innerHTML = results.join(' '); }; buttonEl.addEventListener('click', () => renderResultsToDom(generateArrayAndRandomize(), resultsEl));

Randomize!

0 1 2 3 4 5 6 7 8 9


Nice one, but does generate a complete random elements every time?
Not quite sure if I understood you correctly. This approach will indeed shuffle the array in a random way (albeit pseudo-random) every time you call the sort array - it's not a stable sort, for obvious reasons.
For the same reasons as explained at stackoverflow.com/a/18650169/28234 . This is much more likely to leave early elements near the start of the array.
This is a great, easy one-liner for when you need to scramble an array, but don't care too much about having the results be academically provably random. Sometimes, that last few inches to perfection take more time than it's worth.
It would be lovely if this worked, but it doesn't. Because of the way quick-search works, an inconsistent comparator will be likely to leave array elements close to their original position. Your array will not be scrambled.
h
hexacyanide

Adding to @Laurens Holsts answer. This is 50% compressed.

function shuffleArray(d) {
  for (var c = d.length - 1; c > 0; c--) {
    var b = Math.floor(Math.random() * (c + 1));
    var a = d[c];
    d[c] = d[b];
    d[b] = a;
  }
  return d
};

We should be encouraging people to use _.shuffle rather than pasting code from stack overflow; and, we should be discouraging people from compressing their stack overflow answers. That's what jsmin is for.
@DavidJones: Why would I include an entire 4kb library just to shuffle an array?
@KingKongFrog name calling is also not conductive to a assemblage of a reasonable community.
is it efficient to do var b = in a loop instead of declaring b outside loop and assigning it with b = in a loop?
@Brian Won't make a difference; the hoisting happens when the source code is parsed. No probably involved.
B
BrunoLM

With ES2015 you can use this one:

Array.prototype.shuffle = function() {
  let m = this.length, i;
  while (m) {
    i = (Math.random() * m--) >>> 0;
    [this[m], this[i]] = [this[i], this[m]]
  }
  return this;
}

Usage:

[1, 2, 3, 4, 5, 6, 7].shuffle();

To truncate, you should use n >>> 0 instead of ~~n. Array indices can be higher than 2³¹-1.
Destructuring like this makes for such a clean implementation +1
D
Daniel Martin

I found this variant hanging out in the "deleted by author" answers on a duplicate of this question. Unlike some of the other answers that have many upvotes already, this is:

Actually random Not in-place (hence the shuffled name rather than shuffle) Not already present here with multiple variants

Here's a jsfiddle showing it in use.

Array.prototype.shuffled = function() {
  return this.map(function(n){ return [Math.random(), n] })
             .sort().map(function(n){ return n[1] });
}

(I suspect it was deleted as it is a very inefficient way to randomize the array, especially for larger arrays... whereas the accepted answer, and a number of other clones of that answer randomize in-place).
Yeah, but given that the well-known wrong answer is still up with a bunch of votes, an inefficient but correct solution should at least be mentioned.
[1,2,3,4,5,6].sort(function() { return .5 - Math.random(); }); - it doesn't give a random sort, and if you use it you can end up embarrassed: robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
You need to use .sort(function(a,b){ return a[0] - b[0]; }) if you want the sort to compare values numerically. The default .sort() comparator is lexicographic, meaning it will consider 10 to be less than 2 since 1 is less than 2.
@4castle Okay, I updated the code, but am going to revert it: the distinction between lexicographic order and numerical order doesn't matter for numbers in the range that Math.random() produces. (that is, lexicographic order is the same as numeric order when dealing with numbers from 0 (inclusive) to 1 (exclusive))
h
hakkikonu
//one line solution
shuffle = (array) => array.sort(() => Math.random() - 0.5);


//Demo
let arr = [1, 2, 3];
shuffle(arr);
alert(arr);

https://javascript.info/task/shuffle

Math.random() - 0.5 is a random number that may be positive or negative, so the sorting function reorders elements randomly.


This does not shuffle with homogeneous probability distribution.
It is also a repeat of this older answer. and this still older answer No need to repeat a bad algorithm.
C
Charlie Wallace
var shuffle = function(array) {
   temp = [];
   originalLength = array.length;
   for (var i = 0; i < originalLength; i++) {
     temp.push(array.splice(Math.floor(Math.random()*array.length),1));
   }
   return temp;
};

This is obviously not as optimal as the Fisher-Yates algorithm, but would it work for technical interviews?
@Andrea The code was broken due to the fact that array length is changed inside the for loop. With the last edit this is corrected.
You didn't declare your variables, which makes them globals - and this function seems to randomly remove elements from the input array.
E
Enijar

Here is the EASIEST one,

function shuffle(array) {
  return array.sort(() => Math.random() - 0.5);
}

for further example, you can check it here


Looks a lot like this older answer...
Not to mention this answer. No need to repeat... Moreover this method does not provide a homogeneous probability distribution.
C
Casimir et Hippolyte

benchmarks

Let's first see the results then we'll look at each implementation of shuffle below -

splice is slow

Any solution using splice or shift in a loop is going to be very slow. Which is especially noticeable when we increase the size of the array. In a naive algorithm we -

get a rand position, i, in the input array, t add t[i] to the output splice position i from array t

To exaggerate the slow effect, we'll demonstrate this on an array of one million elements. The following script almost 30 seconds -

const shuffle = t => Array.from(sample(t, t.length)) function* sample(t, n) { let r = Array.from(t) while (n > 0 && r.length) { const i = rand(r.length) // 1 yield r[i] // 2 r.splice(i, 1) // 3 n = n - 1 } } const rand = n => Math.floor(Math.random() * n) function swap (t, i, j) { let q = t[i] t[i] = t[j] t[j] = q return t } const size = 1e6 const bigarray = Array.from(Array(size), (_,i) => i) console.time("shuffle via splice") const result = shuffle(bigarray) console.timeEnd("shuffle via splice") document.body.textContent = JSON.stringify(result, null, 2) body::before { content: "1 million elements via splice"; font-weight: bold; display: block; }

pop is fast

The trick is not to splice and instead use the super efficient pop. To do this, in place of the typical splice call, you -

select the position to splice, i swap t[i] with the last element, t[t.length - 1] add t.pop() to the result

Now we can shuffle one million elements in less than 100 milliseconds -

const shuffle = t => Array.from(sample(t, t.length)) function* sample(t, n) { let r = Array.from(t) while (n > 0 && r.length) { const i = rand(r.length) // 1 swap(r, i, r.length - 1) // 2 yield r.pop() // 3 n = n - 1 } } const rand = n => Math.floor(Math.random() * n) function swap (t, i, j) { let q = t[i] t[i] = t[j] t[j] = q return t } const size = 1e6 const bigarray = Array.from(Array(size), (_,i) => i) console.time("shuffle via pop") const result = shuffle(bigarray) console.timeEnd("shuffle via pop") document.body.textContent = JSON.stringify(result, null, 2) body::before { content: "1 million elements via pop"; font-weight: bold; display: block; }

even faster

The two implementations of shuffle above produce a new output array. The input array is not modified. This is my preferred way of working however you can increase the speed even more by shuffling in place.

Below shuffle one million elements in less than 10 milliseconds -

function shuffle (t) { let last = t.length let n while (last > 0) { n = rand(last) swap(t, n, --last) } } const rand = n => Math.floor(Math.random() * n) function swap (t, i, j) { let q = t[i] t[i] = t[j] t[j] = q return t } const size = 1e6 const bigarray = Array.from(Array(size), (_,i) => i) console.time("shuffle in place") shuffle(bigarray) console.timeEnd("shuffle in place") document.body.textContent = JSON.stringify(bigarray, null, 2) body::before { content: "1 million elements in place"; font-weight: bold; display: block; }


J
Julian K.

A recursive solution:

function shuffle(a,b){
    return a.length==0?b:function(c){
        return shuffle(a,(b||[]).concat(c));
    }(a.splice(Math.floor(Math.random()*a.length),1));
};

c
coredumperror

Fisher-Yates shuffle in javascript. I'm posting this here because the use of two utility functions (swap and randInt) clarifies the algorithm compared to the other answers here.

function swap(arr, i, j) { 
  // swaps two elements of an array in place
  var temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}
function randInt(max) { 
  // returns random integer between 0 and max-1 inclusive.
  return Math.floor(Math.random()*max);
}
function shuffle(arr) {
  // For each slot in the array (starting at the end), 
  // pick an element randomly from the unplaced elements and
  // place it in the slot, exchanging places with the 
  // element in the slot. 
  for(var slot = arr.length - 1; slot > 0; slot--){
    var element = randInt(slot+1);
    swap(arr, element, slot);
  }
}

i
icl7126

Modern short inline solution using ES6 features:

['a','b','c','d'].map(x => [Math.random(), x]).sort(([a], [b]) => a - b).map(([_, x]) => x);

(for educational purposes)


what's the distribution of this one?
@chovy To explain what is happening, we generate random number for each item in the array and then sort the items by that number. So as long as you are getting "real random" numbers from the Math.random() function, you will get an uniform distribution (each item has the same chance to be at any position).
M
Milo Wielondek

First of all, have a look here for a great visual comparison of different sorting methods in javascript.

Secondly, if you have a quick look at the link above you'll find that the random order sort seems to perform relatively well compared to the other methods, while being extremely easy and fast to implement as shown below:

function shuffle(array) {
  var random = array.map(Math.random);
  array.sort(function(a, b) {
    return random[array.indexOf(a)] - random[array.indexOf(b)];
  });
}

Edit: as pointed out by @gregers, the compare function is called with values rather than indices, which is why you need to use indexOf. Note that this change makes the code less suitable for larger arrays as indexOf runs in O(n) time.


Array.prototype.sort passes in two values as a and b, not the index. So this code doesn't work.
@gregers you're right, I've edited the answer. Thanks.
This is not very random. Depending on the implementation of sort, an element at the lowest array index might require more comparisons in order to get to the highest index than the element next to the highest index. This means that it is less likely for the element at the lowest index to get to the highest index.
M
Marcin Malinowski

All the other answers are based on Math.random() which is fast but not suitable for cryptgraphic level randomization.

The below code is using the well known Fisher-Yates algorithm while utilizing Web Cryptography API for cryptographic level of randomization.

var d = [1,2,3,4,5,6,7,8,9,10]; function shuffle(a) { var x, t, r = new Uint32Array(1); for (var i = 0, c = a.length - 1, m = a.length; i < c; i++, m--) { crypto.getRandomValues(r); x = Math.floor(r / 65536 / 65536 * m) + i; t = a [i], a [i] = a [x], a [x] = t; } return a; } console.log(shuffle(d));


E
Evgeniya Manolova

a shuffle function that doesn't change the source array

Update: Here I'm suggesting a relatively simple (not from complexity perspective) and short algorithm that will do just fine with small sized arrays, but it's definitely going to cost a lot more than the classic Durstenfeld algorithm when you deal with huge arrays. You can find the Durstenfeld in one of the top replies to this question.

Original answer:

If you don't wish your shuffle function to mutate the source array, you can copy it to a local variable, then do the rest with a simple shuffling logic.

function shuffle(array) {
  var result = [], source = array.concat([]);

  while (source.length) {
    let index = Math.floor(Math.random() * source.length);
    result.push(source[index]);
    source.splice(index, 1);
  }

  return result;
}

Shuffling logic: pick up a random index, then add the corresponding element to the result array and delete it from the source array copy. Repeat this action until the source array gets empty.

And if you really want it short, here's how far I could get:

function shuffle(array) {
  var result = [], source = array.concat([]);

  while (source.length) {
    let index = Math.floor(Math.random() * source.length);
    result.push(source.splice(index, 1)[0]);
  }

  return result;
}

This is essentially the original Fisher-Yates algorithm, with your splice being a horribly inefficient way to do what they called "striking out". If you don't want to mutate the original array, then just copy it, and then shuffle that copy in place using the much more efficient Durstenfeld variant.
@torazaburo, thank you for your feedback. I've updated my answer, to make it clear that I'm rather offering a nice-looking solution, than a super-scaling one
We could also use the splice method to create a copy like so: source = array.slice();.
E
Erik Martín Jordán

Using Fisher-Yates shuffle algorithm and ES6:

// Original array
let array = ['a', 'b', 'c', 'd'];

// Create a copy of the original array to be randomized
let shuffle = [...array];

// 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
shuffle.forEach( (elem, i, arr, j = getRandomValue(i, arr.length)) => [arr[i], arr[j]] = [arr[j], arr[i]] );

console.log(shuffle);
// ['d', 'a', 'b', 'c']

Great and easy to understand.
R
Rafi Henig

You can arbitrarily decide whether to return 1 : -1 by using Math.random:

[1, 2, 3, 4].sort(() => (Math.random() > 0.5) ? 1 : -1)

Try running the following example:

const array = [1, 2, 3, 4]; const shuffeled = array.sort(() => { const randomTrueOrFalse = Math.random() > 0.5; return randomTrueOrFalse ? 1 : -1 }); console.log(shuffeled);


Is this unbiased?
what? this is so pointless. it has almost 0 chance of leaving the element intact (random generating exactly 0.5)
T
Tính Ngô Quang

You can do it easily with:

// array var fruits = ["Banana", "Orange", "Apple", "Mango"]; // random fruits.sort(function(a, b){return 0.5 - Math.random()}); // out console.log(fruits);

Please reference at JavaScript Sorting Arrays


This algorithm has long been proven to be defective.
Please prove to me. I based on w3schools
You could read the thread at css-tricks.com/snippets/javascript/shuffle-array, or at news.ycombinator.com/item?id=2728914. W3schools has always been, and remains, a horrible source of information.
For a good discussion on why this is not a good approach see stackoverflow.com/questions/962802/…
Y
Yevgen Gorbunkov

We're still shuffling arrays in 2019, so here goes my approach, which seems to be neat and fast to me:

const src = [...'abcdefg']; const shuffle = arr => [...arr].reduceRight((res,_,__,s) => (res.push(s.splice(0|Math.random()*s.length,1)[0]), res),[]); console.log(shuffle(src)); .as-console-wrapper {min-height: 100%}


B
BBaysinger

A simple modification of CoolAJ86's answer that does not modify the original array:

 /**
 * Returns a new array whose contents are a shuffled copy of the original array.
 * @param {Array} The items to shuffle.
 * https://stackoverflow.com/a/2450976/1673761
 * https://stackoverflow.com/a/44071316/1673761
 */
const shuffle = (array) => {
  let currentIndex = array.length;
  let temporaryValue;
  let randomIndex;
  const newArray = array.slice();
  // While there remains elements to shuffle...
  while (currentIndex) {
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;
    // Swap it with the current element.
    temporaryValue = newArray[currentIndex];
    newArray[currentIndex] = newArray[randomIndex];
    newArray[randomIndex] = temporaryValue;
  }
  return newArray;
};

R
Raphael C

yet another implementation of Fisher-Yates, using strict mode:

function shuffleArray(a) {
    "use strict";
    var i, t, j;
    for (i = a.length - 1; i > 0; i -= 1) {
        t = a[i];
        j = Math.floor(Math.random() * (i + 1));
        a[i] = a[j];
        a[j] = t;
    }
    return a;
}

What value does the addition of use strict provide over the accepted answer?
To learn more about strict mode and how it influences performance you can read about it here: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Hmm, could you point to something specific from the referenced document? Nothing in there seems to reference "improving performance," aside from a vague comment at the top about potentially making it difficult for the js engine to optimize. In this case, it's unclear to me what use strict would improve.
Strict mode has been around for quite some time, and there are sufficient reads out there for anyone to make their own opinion if they should always use it or not and why. Jslint for instance makes it clear enough that you should always use strict mode. Douglas Crockford has written quite an amount of articles and some great videos on why it is important to always use strict mode not only as a good practice but also how it is interpreted differently by browser js engines such as V8. I strongly advise you to Google it and make your own opinion about it.
Here is an old thread about perfs in strict mode, a bit old but still relevant: stackoverflow.com/questions/3145966/…
v
vickisys

Randomize array

 var arr = ['apple','cat','Adam','123','Zorro','petunia']; 
 var n = arr.length; var tempArr = [];

 for ( var i = 0; i < n-1; i++ ) {

    // The following line removes one random element from arr 
     // and pushes it onto tempArr 
     tempArr.push(arr.splice(Math.floor(Math.random()*arr.length),1)[0]);
 }

 // Push the remaining item onto tempArr 
 tempArr.push(arr[0]); 
 arr=tempArr; 

There shouldn't be a -1 for n as you used < not <=
i
iPhoney

For those of us who are not very gifted but have access to the wonders of lodash, there is such a thing as lodash.shuffle.