1993

I have an array like this:

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

How can I randomize / shuffle it?

TheMaster
  • 45,448
  • 6
  • 62
  • 85
Ali
  • 261,656
  • 265
  • 575
  • 769
  • 13
    Just throwing this here that you can visualize how random a shuffle function actually is with this visualizer Mike Bostock made: http://bost.ocks.org/mike/shuffle/compare.html – aug Dec 10 '14 at 19:42
  • 6
    @Blazemonger jsPref is dead. Can you just post here which is the fastest? – eozzy Sep 28 '16 at 01:06
  • 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:51
  • The reduce solution has O(n^2) complexity. Try running it on an array with a million elements. – riv Jul 02 '18 at 14:02
  • The reduce solution is super elegant, but also appears to be wrong - for example: `[1,2].reduce((a,v)=>a.splice(Math.floor(Math.random(7) * a.length), 0, v) && a, []) ` ALWAYS returns [2,1] and for any other list it never retains the 0th element in the 0th position. I can't quite figure out why. – Mitra Ardron Jul 26 '18 at 02:38
  • 43
    How about this? `arr1.sort(() => (Math.random() > .5) ? 1 : -1);` – yuval.bl Sep 26 '18 at 17:18
  • @yuval.bl Elegant solution. But this is _O(n.logn)_. The accepted answer is _O(n)_. – Emre Tapcı Aug 13 '21 at 18:50
  • 19
    a short answer would be `a.sort(() => Math.random() - 0.5)` – SaboSuke Sep 03 '21 at 18:52
  • 2
    @yuval.bl This method is breaking a contract required by the ES specification: "Calling comparefn(a, b) always returns the same value v when given a specific pair of values a and b as its two arguments." See here: https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.sort – The Vee Nov 03 '21 at 11:56
  • 4
    @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" – yuval.bl Nov 11 '21 at 16:12
  • 4
    Do not use Array.sort() with Math.random(). It does not randomly sort the array. Demo: https://codepen.io/joewestwood/pen/vYRmzVK Use a method that produces random arrangements. – Splendiferous Jul 21 '22 at 11:00

71 Answers71

2407

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.

Rufflewind
  • 8,545
  • 2
  • 35
  • 55
ChristopheD
  • 112,638
  • 29
  • 165
  • 179
  • 19
    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 Jun 08 '11 at 07:21
  • 64
    @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](http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle) where there are other implementations that also skip the first element. Also check out [this](http://www.codinghorror.com/blog/2007/12/the-danger-of-naivete.html) article which talks about why it is important for the loop not to run for the first element. – theon Jul 20 '12 at 12:57
  • 1
    Be sure to transpile if you're going to do destructuring assignments in a busy loop -- allocating objects is expensive. – ggorlen Jul 25 '21 at 22:18
  • @ggorlen What do you mean by transpiling in this context? Can you give us an example or further explanation? – nkhil Oct 04 '21 at 16:12
  • @ggorlen Thanks! I understand now - I was confused as I usually use Node so didn't immediately think of the browser. Thanks for clarifying! – nkhil Oct 04 '21 at 17:14
  • This is a good display of the theory, but an actual implementation of this should check that the input is actually an array. As it is, if you accidentally pass in a value of a different type, the while loop will never finish and the program will hang. – Isaac King Jan 11 '22 at 22:32
  • 9
    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. – Sam Mar 14 '22 at 03:18
  • what is the big O for this algorithm? – Rongeegee Oct 04 '22 at 18:39
  • I think the biggest problem with this answer is that it returns `array` even though it modifies the array in place. Better not to return anything then to highlight that behavior. – bersling Jan 27 '23 at 12:30
1163

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]];
    }
}
ashleedawg
  • 20,365
  • 9
  • 72
  • 105
Laurens Holst
  • 20,156
  • 2
  • 29
  • 33
  • 14
    The implementation in this answer favors the lower end of the array. [Found out the hard way](http://softwareonastring.com/1135/perils-of-copy-paste-programming). `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?](http://stackoverflow.com/a/1527820/11225) for a very comprehensive explanation. – Marjan Venema Dec 18 '16 at 20:17
  • 47
    @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 https://blog.codinghorror.com/the-danger-of-naivete/ for a nice writeup of this mistake. – user94559 Mar 11 '17 at 01:44
  • 5
    repeating user94559's comment with references https://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) – RedPandaCurios May 19 '21 at 08:32
  • 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]]}}` – ashleedawg Oct 24 '21 at 04:47
  • Could we do here `let i = array.length;` instead of `let i = array.length - 1;` in the for loop and then `const j = Math.floor(Math.random() * i);` on the next line? – ivan milenkovic Jan 21 '22 at 15:21
  • 4
    Did you forget to add `return array;` ? – IRvanFauziE Feb 09 '22 at 16:38
  • 2
    @IRvanFauziE The function works on the array you pass it. Adding `return array` will only allow for easier chaining with other . – AgainPsychoX Feb 13 '22 at 06:58
  • if using arrow function we should return array – Nanda Z May 29 '22 at 03:58
  • This solution was significantly faster, and often less complex, when I compared it to other answers here. +1 – Yogi Jun 19 '22 at 12:37
  • 1
    @IRvanFauziE looks like, `return array;` is not required, as it is `in-place` algorithm, that is, the passed-in array as parameter is changed permanently. – Manohar Reddy Poreddy Sep 17 '22 at 05:46
  • @ashleedawg is the `compressed` function slower than normal? does it create temporary 2-element arrays, in each iteration of loop? – Manohar Reddy Poreddy Sep 17 '22 at 05:48
  • This is Fisher-Yates shuffle algorithm. The function loops through the array and for each element, it generates a random index between 0 and the current index, and swaps the current element with the element at the random index. This way, the order of the elements in the array is shuffled randomly. – Anurag Mishra Jan 23 '23 at 08:06
  • 1
    here's the same algorithm as in the ES6 update, but with typescript and not in place: `export function shuffleArray(array: T[]): T[] { const arrayCopy = [...array]; for (let i = arrayCopy.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [arrayCopy[i], arrayCopy[j]] = [arrayCopy[j], arrayCopy[i]]; } return arrayCopy; } ` – bersling Jan 27 '23 at 12:39
427

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)
   
console.log(shuffled)
  1. We put each element in the array in an object, and give it a random sort key
  2. We sort using the random key
  3. 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.

superluminary
  • 47,086
  • 25
  • 151
  • 148
  • 23
    Very nice. This is the [Schwartzian transform](https://en.wikipedia.org/wiki/Schwartzian_transform) in js. – Mark Grimes Jun 29 '18 at 10:43
  • 18
    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. – Software Engineer Sep 01 '21 at 13:43
  • 1
    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 – Ilja KO Mar 23 '22 at 09:51
  • 1
    @IljaKO 2N is still O(N), which is less than the time complexity of O(N log N) – random_0620 Apr 24 '22 at 01:40
  • 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) – Ilja KO Apr 24 '22 at 09:57
  • This won't work. `.sort` modifies the original array. It doesn't work as `.map`. You have to create a new array in the first map. then sort it, and then return the last mapping. Something like this: const randomize = array => { const data = array.map(value => ({ value, sort: Math.random() })); data.sort((a, b) => a.sort - b.sort); return data.map(({ value }) => value); }; – Broda Noel May 18 '22 at 03:01
  • @BrodaNoel The first map creates a new array containing the original data and a random number, the sort sorts according to that random number, the second map pops the original object back out into a new array. You can try it in the console if you like. – superluminary May 19 '22 at 13:44
  • @superluminary oh! yes. you are right. I thought `.sort` doesn't return the sorted array. Anyways, be careful there because that's not a clean way to do it. `.sort` make an "in place" modification. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#return_value – Broda Noel May 24 '22 at 15:39
  • @BrodaNoel - It doesn't matter because I've already mapped it in the previous step, so I've already enforced immutability. – superluminary May 27 '22 at 10:01
  • 8
    @IljaKO - O(2N + 2(N log N)) simplifies to O(N log N), so this is truly O(N log N). Big O notation is all about the largest scaling factor. We remove constants because they don't scale with input size, and simplify to the largest single scaling factor. Big O notation is deliberately NOT all about the details. – superluminary Jun 10 '22 at 11:51
  • Could you anyone please explain what's happening on the second line? It's "a.sort - b.sort", he is calling sort within sort? what actually is "a.sort" and "b.sort" here? – Shahzad Rahim Jul 10 '22 at 13:56
  • @ShahzadRahim - In the first line we map the data into a set of objects with sort attributes. In the second line, we sort by those sort attributes. In the third line, we map the data back out of those objects to get rid of the sort attributes. I'd encourage you to run the first line alone in your console to see what you get. It should make more sense when you do that. – superluminary Jul 14 '22 at 13:07
  • 1
    It strikes me that, given that the sort key is being added in one map and then stripped out in another, you could simply call unshuffled.sort((_a, _b) => Math.random() - Math.random()) to achieve the same result. As both a and b's sort properties are just holding the result of independent calls to Math.random() I think it would be equivalent to call it twice separately at call time in the .sort callback. – alex.h Nov 14 '22 at 00:41
  • 1
    Or really just unshuffled.sort(() => Math.random() - Math.random()) – alex.h Nov 14 '22 at 00:42
  • @alex.h - no, it really isn't. In your code you're regenerating random numbers each time, so swapping subtrees arbitrarily on each recursion. In practice this will not produce a genuinely shuffled array. More on this in the rest of the thread. – superluminary Nov 14 '22 at 16:39
  • ah yes on second read, that's quite evident – alex.h Nov 19 '22 at 00:46
  • @alex.h or even unshuffled.sort(() => Math.random() - 0.5) – BlobKat Dec 12 '22 at 15:56
  • @BlobKat - as mentioned elsewhere in this thread, this will not give you the result you are looking for. – superluminary Dec 13 '22 at 11:30
  • @superluminary I was merely suggesting a more concise version of alex.h's solution, I didn't mean it as a viable solution for OP's question – BlobKat Dec 13 '22 at 20:28
  • 1
    @BlobKat - sorry for the misunderstanding. – superluminary Dec 15 '22 at 14:22
  • 1
    I like this approach for shorter arrays because you can implement some logic in that sorting to bias the sort order and still expect something randomish. – nebulae Dec 26 '22 at 21:40
  • 1
    this the way. should be the preferred solution. – Flo rian Apr 16 '23 at 07:08
229

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.

Rick
  • 7,007
  • 2
  • 49
  • 79
deadrunk
  • 13,861
  • 4
  • 29
  • 29
  • 208
    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% – radtad Nov 13 '13 at 18:35
  • 30
    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. – deadrunk Nov 21 '13 at 00:37
  • 25
    It's also the [least efficient of all the methods available](http://jsperf.com/array-shuffle-comparator/5). – Blazemonger Dec 17 '13 at 14:21
  • 15
    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). – MatsLindh Sep 10 '14 at 14:07
  • 5
    Essential reading: [Is it correct to use JavaScript Array.sort() method for shuffling?](http://stackoverflow.com/q/962802/1048572) – Bergi Apr 03 '15 at 20:48
  • 2
    I upvoted without much testing because of its simplicity – Naga Feb 20 '22 at 18:21
  • Same as above, I don't need much of a cryptographic shuffle. Something simple for a small game with no huge consequences if the randomness is not 100% on point. – kissu Dec 06 '22 at 14:52
76

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();
hexacyanide
  • 88,222
  • 31
  • 159
  • 162
vn_grv
  • 901
  • 6
  • 5
74

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;
}
Lukas Liesis
  • 24,652
  • 10
  • 111
  • 109
con
  • 2,358
  • 25
  • 33
  • 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". https://stackoverflow.com/questions/14034180/why-is-extending-native-objects-a-bad-practice – Lukas Liesis Mar 15 '22 at 10:09
  • 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. – Got To Figure May 15 '22 at 20:59
67

NEW!

Shorter & probably *faster Fisher-Yates shuffle algorithm

  1. it uses while---
  2. bitwise to floor (numbers up to 10 decimal digits (32bit))
  3. 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
 }
}
Community
  • 1
  • 1
cocco
  • 16,442
  • 7
  • 62
  • 77
  • 7
    check out the performance ... 2x faster on most browsers... but needs more jsperf testers... – cocco Sep 23 '14 at 11:20
  • 11
    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. – cocco Sep 23 '14 at 11:29
  • 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. http://jsperf.com/fyshuffle/3 – Spig Oct 09 '14 at 18:49
  • 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... – cocco Oct 09 '14 at 19:03
  • mobile safari 1409(fy) vs (shuffle)1253 ,ie 31850(fy) vs (shuffle)13405 – cocco Oct 09 '14 at 19:05
  • actually shuffle at the other side it's almost the same as my fy function except the bitwise and while. – cocco Oct 09 '14 at 19:07
  • note that also the for loop works alot better on chrome than on ANY other browser. just setup a performance.time() or how it's called and test on several different browsrers... only in chrome the for loop is faster/same speed than while – cocco Oct 09 '14 at 19:09
  • btw i also got my mac mini with better results using fy, and btw what do you mean you swapped fy&shuffle prototype? changed the execution position in jsperf?? – cocco Oct 09 '14 at 19:11
  • ohh i was testing on safari 6.1 where it's also 90% faster. looks like they changed something in the 7.1 – cocco Oct 09 '14 at 19:13
  • atm i can't test on the safari 6.1 mashine – cocco Oct 09 '14 at 19:14
  • if i tell you the i have seen an animal in africa which has big ears and a very long nose. you would understand that it is an elefant. but the english language, also other languages, allow you to use the word elefant. javascript is also a language that has many words that can be used in different ways. i just try to use the proper words for that language. short code and performance is always important in this language. i don't use minifiers. i reread my code and optimize it. – cocco Nov 24 '14 at 17:08
  • So should you . if you don't understand something you could ask me to explain the code. Or just grab a book and read why i don't use all those multiple var, closures and extra bytes. Another thing... downvote if you think this is not a good code. noone stops you – cocco Nov 24 '14 at 17:09
  • @cocco Why have you called `numberArray(a,b)` with only single parameter `numberArray(10)` in your [fiddle](http://jsfiddle.net/vvpoma8w/2/)? – Zameer Ansari Jan 26 '16 at 15:49
  • 1
    This is mental and amazing in equal measure. I've been writing JS for 20 years, and I learned some new tricks reading this just now. I'm not sure whether I've been enlightened or ruined forever. – superluminary Sep 23 '21 at 19:48
  • 1
    If your software does something useful besides shuffling arrays, micro-optimizations doesn't matter. If your software just shuffles arrays without any other effects, it's a waste of human time ‍♂️ – meandre Nov 26 '21 at 14:58
  • `for` is better than `while` for code golf `function fy(a,b,c){for(c=a.length;c;[a[c],a[b]]=[a[b],a[c]])b=Math.random()*c--|0}` – Yukulélé Mar 25 '22 at 17:59
  • I would rather leave it to a minifier instead of minifying my code myself. – Salman A Jun 01 '22 at 10:58
66

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]
    )
}
ns16
  • 1,322
  • 2
  • 17
  • 26
Ben Carp
  • 24,214
  • 9
  • 60
  • 72
  • 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 Sep 11 '17 at 19:00
  • @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. – Ben Carp Sep 12 '17 at 02:47
  • Credit to @sheriffderek who suggested the ascending Algorithm. The ascending algorithm could be proved in induction. – Ben Carp Sep 15 '17 at 01:00
43

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));
<h1>Randomize!</h1>
<button id="trigger">Generate</button>
<p id="results">0 1 2 3 4 5 6 7 8 9</p>
Ben Carp
  • 24,214
  • 9
  • 60
  • 72
Kris Selbekk
  • 7,438
  • 7
  • 46
  • 73
  • Nice one, but does generate a complete random elements every time? – Playdome.io Apr 10 '17 at 20:00
  • 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. – Kris Selbekk Apr 11 '17 at 11:00
  • 6
    For the same reasons as explained at https://stackoverflow.com/a/18650169/28234 . This is much more likely to leave early elements near the start of the array. – AlexC Jun 23 '17 at 10:41
  • 7
    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. – Daniel Griscom Nov 03 '17 at 18:48
  • 1
    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. – superluminary Mar 14 '18 at 14:34
  • Yet it is scrambled though. Try running `[1,2,3,4,5].sort(() => Math.random() * 2 - 1);` in the console, it'll scramble the result for every time. This isn't the perfect randomizer, but for simple use-cases it works just fine. – Kris Selbekk Mar 16 '18 at 13:09
  • I don't think the comments here adequately warn how bad this method is if you want an even vaguely fair shuffle. Testing this method on chrome with an array of ten elements, the last element stays the last element HALF the time, and 75% of the time is in the last two. This isn't "academic" - it's really quite bad. OTOH, if you want to subtly make something that seems random but lets you cheat by often knowing characteristics of the supposedly shuffled list, I guess this could be used for that. – Daniel Martin Jun 22 '18 at 06:31
26

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
};
hexacyanide
  • 88,222
  • 31
  • 159
  • 162
KingKongFrog
  • 13,946
  • 21
  • 75
  • 124
  • 8
    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. – David Jones Apr 05 '13 at 08:28
  • 60
    @DavidJones: Why would I include an entire 4kb library just to shuffle an array? – Blender May 04 '13 at 19:23
  • 2
    is it efficient to do `var b = ` in a loop instead of declaring b outside loop and assigning it with `b = ` in a loop? – Alex K Oct 28 '13 at 09:51
  • 1
    @Alex: Javascript doesn't actually support block-scope variables (see [Why does JavaScript not have block scope?](http://stackoverflow.com/q/17311693/18192)). So, declaring b outside the loop probably won't make a difference. – Brian Jul 12 '14 at 04:17
  • 2
    @Brian *Won't* make a difference; the hoisting happens when the source code is parsed. No probably involved. – user2864740 Sep 15 '14 at 04:18
  • Could you please tell me which one you think is better for shuffling? (Link: http://ideone.com/pfRanN). As i tested, there is no difference between them. – Hengameh Sep 22 '15 at 00:33
25

Warning!
Using this answer for randomizing large arrays, cryptography, or any other application requiring true randomness is not recommended, due to its bias and inefficiency. Elements position is only semi-randomized, and they will tend to stay closer to their original position. See https://stackoverflow.com/a/18650169/28234.


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];

// Based on the value returned by Math.Random,
// the decision is arbitrarily made whether to return 1 : -1

const shuffeled = array.sort(() => {
  const randomTrueOrFalse = Math.random() > 0.5;
  return randomTrueOrFalse ? 1 : -1
});

console.log(shuffeled);
Rafi Henig
  • 5,950
  • 2
  • 16
  • 36
  • Is this unbiased? – Juzer Ali Jun 21 '19 at 06:23
  • what? this is so pointless. it has almost 0 chance of leaving the element intact (random generating exactly 0.5) – user151496 Nov 02 '19 at 02:04
  • I suggest removing this answer. Answer isn't correct and isn't new. The other wrong answer is left for future reference so I think this one can be removed :-) – Ben Carp Oct 10 '22 at 11:14
  • @BenCarp First of all, your comment and suggestion are greatly appreciated! They will be reviewed and considered (as for your assertion that my answer isn't new, I believe it differs from the one you referred to) – Rafi Henig Oct 10 '22 at 20:25
  • @RafiHenig The difference from the other answer is very very minor. Given that both are incorrect, nothing less, I don't see the point of it. – Ben Carp Oct 11 '22 at 13:45
  • Your comment sounds as if in some circumstances using this solution makes sense. I can't think of such. For it doesn't lead to randomization and is much more expensive. It might be suitable if someone would ask for a semi randomization that keeps elements close to theiry original position, but that is not the question before us. – Ben Carp Oct 11 '22 at 13:47
  • @BenCarp Based on my observations, this algorithm works well for both randomizing small arrays of data as well as for situations in which it is not essential to apply true randomness to the array in question. – Rafi Henig Oct 11 '22 at 15:35
24

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:

  1. Actually random
  2. Not in-place (hence the shuffled name rather than shuffle)
  3. 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] });
}
Daniel Martin
  • 23,083
  • 6
  • 50
  • 70
  • (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). – WiredPrairie Jul 14 '15 at 12:17
  • 1
    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. – Daniel Martin Jul 14 '15 at 18:54
  • `[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: http://www.robweir.com/blog/2010/02/microsoft-random-browser-ballot.html – Daniel Martin Jul 14 '15 at 22:58
  • 3
    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 Nov 10 '17 at 14:39
  • 1
    @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)) – Daniel Martin Nov 10 '17 at 14:56
  • This is a great answer, _simple and understandable_, and fast-enough for non-huge arrays. Thanks! BTW, the final `.map()` can be simplified to `.map(([r, v]) => v )` – user949300 Jan 08 '22 at 07:51
  • Can be simplified to `a.map(r=>[Math.random(),r]).sort().map(r=>r[1])`. This is 6 to 9 times slower than the Fisher-Yates though. – undefined Mar 11 '22 at 02:14
23

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();
BrunoLM
  • 97,872
  • 84
  • 296
  • 452
22

benchmarks

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

  • splice

  • pop

  • inplace


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 -

  1. get a rand position, i, in the input array, t
  2. add t[i] to the output
  3. 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 =>
  0 | 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 -

  1. select the position to splice, i
  2. swap t[i] with the last element, t[t.length - 1]
  3. 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 =>
  0 | 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 =>
  0 | 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;
}
Mulan
  • 129,518
  • 31
  • 228
  • 259
20
//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.

hakki
  • 6,181
  • 6
  • 62
  • 106
  • 1
    This does not shuffle with homogeneous probability distribution. – trincot Sep 29 '20 at 08:46
  • 9
    It is also a repeat [of this older answer](https://stackoverflow.com/a/43235780/5459839). and [this still older answer](https://stackoverflow.com/a/18650169/5459839) No need to repeat a bad algorithm. – trincot Sep 29 '20 at 08:51
17
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;
};
Charlie Wallace
  • 1,810
  • 1
  • 15
  • 17
Tophe
  • 303
  • 2
  • 4
  • This is obviously not as optimal as the Fisher-Yates algorithm, but would it work for technical interviews? – davidatthepark May 19 '16 at 22:17
  • @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. – Charlie Wallace Mar 20 '19 at 16:41
  • You didn't declare your variables, which makes them globals - and this function seems to randomly remove elements from the input array. – mindplay.dk Apr 06 '21 at 15:08
12

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));
};
Julian K.
  • 131
  • 1
  • 4
11

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)

icl7126
  • 5,740
  • 4
  • 53
  • 51
  • what's the distribution of this one? – chovy Nov 27 '21 at 06:44
  • 1
    @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). – icl7126 Nov 27 '21 at 08:04
10

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);
  }
}
coredumperror
  • 8,471
  • 6
  • 33
  • 42
Daniel Patru
  • 1,968
  • 18
  • 15
10

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']
Erik Martín Jordán
  • 4,332
  • 3
  • 26
  • 36
8

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.

Milo Wielondek
  • 4,164
  • 3
  • 33
  • 45
  • `Array.prototype.sort` passes in two *values* as `a` and `b`, not the index. So this code doesn't work. – gregers Mar 29 '16 at 13:34
  • @gregers you're right, I've edited the answer. Thanks. – Milo Wielondek May 15 '16 at 15:00
  • 1
    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. – 1' OR 1 -- Jul 19 '16 at 00:16
8

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));
8

a shuffle function that doesn't change the source array

Disclaimer

Please note that this solution is not suitable for large arrays! If you are shuffling large datasets, you should use the Durstenfeld algorithm suggested above.

Solution

function shuffle(array) {
  const result = [], itemsLeft = array.concat([]);

  while (itemsLeft.length) {
    const randomIndex = Math.floor(Math.random() * itemsLeft.length);
    const [randomItem] = itemsLeft.splice(randomIndex, 1); // take out a random item from itemsLeft
    result.push(randomItem); // ...and add it to the result
  }

  return result;
}

How it works

  1. copies the initial array into itemsLeft

  2. picks up a random index from itemsLeft, adds the corresponding element to the result array and deletes it from the itemsLeft array

  3. repeats step (2) until itemsLeft array gets empty

  4. returns result

Evgeniya Manolova
  • 2,542
  • 27
  • 21
  • 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. –  Jul 09 '18 at 04:49
  • @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 – Evgeniya Manolova Jul 20 '18 at 11:10
  • We could also use the `splice` method to create a copy like so: `source = array.slice();`. – tg_so Apr 21 '19 at 12:14
  • what is the difference between array.concat([]) and [...array] – Flo rian Apr 16 '23 at 07:49
  • Hi Flo rian, perhaps you can find a decent answer in this thread: https://stackoverflow.com/questions/48865710/spread-operator-vs-array-concat – Evgeniya Manolova Apr 20 '23 at 05:51
7

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.

iPhoney
  • 771
  • 1
  • 10
  • 14
7

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%}
Yevhen Horbunkov
  • 14,965
  • 3
  • 20
  • 42
7

Here is the EASIEST one,

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

for further example, you can check it here

Enijar
  • 6,387
  • 9
  • 44
  • 73
Aljohn Yamaro
  • 2,629
  • 25
  • 22
  • 2
    Looks a lot like [this older answer](https://stackoverflow.com/a/59075967/215552)... – Heretic Monkey Sep 23 '20 at 20:40
  • 4
    Not to mention [this answer](https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array/59075967#59075967). No need to repeat... Moreover this method does not provide a homogeneous probability distribution. – trincot Sep 29 '20 at 08:49
  • From the link you added yourself: "But because the sorting function is not meant to be used this way, not all permutations have the same probability." – Kerwin Sneijders Aug 16 '22 at 08:05
6

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;
};
BBaysinger
  • 6,614
  • 13
  • 63
  • 132
abumalick
  • 2,136
  • 23
  • 27
6

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

Tính Ngô Quang
  • 4,400
  • 1
  • 33
  • 33
  • This algorithm has long been proven to be defective. –  Jul 08 '18 at 07:27
  • Please prove to me. I based on w3schools – Tính Ngô Quang Jul 09 '18 at 05:14
  • 7
    You could read the thread at https://css-tricks.com/snippets/javascript/shuffle-array/, or at https://news.ycombinator.com/item?id=2728914. W3schools has always been, and remains, a horrible source of information. –  Jul 09 '18 at 09:09
  • For a good discussion on why this is not a good approach see https://stackoverflow.com/questions/962802/is-it-correct-to-use-javascript-array-sort-method-for-shuffling – Charlie Wallace Mar 20 '19 at 17:12
6

I found this useful:

const shuffle = (array: any[]) => {
    return array.slice().sort(() => Math.random() - 0.5);
  }
        
console.log(shuffle([1,2,3,4,5,6,7,8,9,10]));
// Output: [4, 3, 8, 10, 1, 7, 9, 2, 6, 5]
Sunny Vakil
  • 362
  • 4
  • 7
5

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;
}
Raphael C
  • 2,296
  • 1
  • 22
  • 22
  • What value does the addition of use strict provide over the accepted answer? – shortstuffsushi Sep 17 '17 at 15:21
  • To learn more about strict mode and how it influences performance you can read about it here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode – Raphael C Sep 18 '17 at 17:55
  • 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. – shortstuffsushi Sep 19 '17 at 15:04
  • 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. – Raphael C Sep 20 '17 at 15:53
  • Here is an old thread about perfs in strict mode, a bit old but still relevant: https://stackoverflow.com/questions/3145966/is-strict-mode-more-performant – Raphael C Sep 20 '17 at 16:06
5

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; 
vickisys
  • 2,008
  • 2
  • 20
  • 27
5

Though there are a number of implementations already advised but I feel we can make it shorter and easier using forEach loop, so we don't need to worry about calculating array length and also we can safely avoid using a temporary variable.

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

myArr.forEach((val, key) => {
  randomIndex = Math.ceil(Math.random()*(key + 1));
  myArr[key] = myArr[randomIndex];
  myArr[randomIndex] = val;
});
// see the values
console.log('Shuffled Array: ', myArr)
Hafizur Rahman
  • 2,314
  • 21
  • 29
5

I use these two methods:

This method does not modify the original array

shuffle(array);

function shuffle(arr) {
    var len = arr.length;
    var d = len;
    var array = [];
    var k, i;
    for (i = 0; i < d; i++) {
        k = Math.floor(Math.random() * len);
        array.push(arr[k]);
        arr.splice(k, 1);
        len = arr.length;
    }
    for (i = 0; i < d; i++) {
        arr[i] = array[i];
    }
    return arr;
}

var arr = ["a", "b", "c", "d"];
arr = shuffle(arr);
console.log(arr);

This method modify the original array

array.shuffle();

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

var arr = ["a", "b", "c", "d"];
arr.shuffle();
console.log(arr);
Blackjack
  • 1,322
  • 1
  • 16
  • 21
4

the shortest arrayShuffle function

function arrayShuffle(o) {
    for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
    return o;
}
Tusko Trush
  • 422
  • 3
  • 15
  • 1
    Apparently you are doing [Sattolo's](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Sattolo.27s_algorithm) instead of [Fisher-Yates](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle) (Knuth, unbiased). – Mingye Wang Oct 18 '16 at 17:32
4

Funny enough there was no non mutating recursive answer:

var shuffle = arr => {
  const recur = (arr,currentIndex)=>{
    console.log("What?",JSON.stringify(arr))
    if(currentIndex===0){
      return arr;
    }
    const randomIndex = Math.floor(Math.random() * currentIndex);
    const swap = arr[currentIndex];
    arr[currentIndex] = arr[randomIndex];
    arr[randomIndex] = swap;
    return recur(
      arr,
      currentIndex - 1
    );
  }
  return recur(arr.map(x=>x),arr.length-1);
};

var arr = [1,2,3,4,5,[6]];
console.log(shuffle(arr));
console.log(arr);
HMR
  • 37,593
  • 24
  • 91
  • 160
  • 3
    Maybe there wasn't because it's pretty inefficient? :-P – Bergi Feb 09 '18 at 04:08
  • @Bergi Correct, updated with first answer logic. Still need to copy the array for immutability. Added because this is flagged as the duplicate of a question asking for a function that takes an array and returned a shuffled array without mutating the array. Now the question actually has an answer the OP was looking for. – HMR Feb 09 '18 at 06:48
4

You can use lodash shuffle. Works like a charm

import _ from lodash;

let numeric_array = [2, 4, 6, 9, 10];
let string_array = ['Car', 'Bus', 'Truck', 'Motorcycle', 'Bicycle', 'Person']

let shuffled_num_array = _.shuffle(numeric_array);
let shuffled_string_array = _.shuffle(string_array);

console.log(shuffled_num_array, shuffled_string_array)
Mayank Pathela
  • 453
  • 1
  • 6
  • 15
3
Array.prototype.shuffle=function(){
   var len = this.length,temp,i
   while(len){
    i=Math.random()*len-- |0;
    temp=this[len],this[len]=this[i],this[i]=temp;
   }
   return this;
}
  • To truncate, you should use `n >>> 0` instead of `n | 0`. Array indices can be higher than 2³¹-1. – Oriol Aug 11 '16 at 21:39
3

From a theoretical point of view, the most elegant way of doing it, in my humble opinion, is to get a single random number between 0 and n!-1 and to compute a one to one mapping from {0, 1, …, n!-1} to all permutations of (0, 1, 2, …, n-1). As long as you can use a (pseudo-)random generator reliable enough for getting such a number without any significant bias, you have enough information in it for achieving what you want without needing several other random numbers.

When computing with IEEE754 double precision floating numbers, you can expect your random generator to provide about 15 decimals. Since you have 15!=1,307,674,368,000 (with 13 digits), you can use the following functions with arrays containing up to 15 elements and assume there will be no significant bias with arrays containing up to 14 elements. If you work on a fixed-size problem requiring to compute many times this shuffle operation, you may want to try the following code which may be faster than other codes since it uses Math.random only once (it involves several copy operations however).

The following function will not be used, but I give it anyway; it returns the index of a given permutation of (0, 1, 2, …, n-1) according to the one to one mapping used in this message (the most natural one when enumerating permuations); it is intended to work with up to 16 elements:

function permIndex(p) {
    var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000];
    var tail = [];
    var i;
    if (p.length == 0) return 0;
    for(i=1;i<(p.length);i++) {
        if (p[i] > p[0]) tail.push(p[i]-1);
        else tail.push(p[i]);
    }
    return p[0] * fact[p.length-1] + permIndex(tail);
}

The reciprocal of the previous function (required for your own question) is below; it is intended to work with up to 16 elements; it returns the permutation of order n of (0, 1, 2, …, s-1):

function permNth(n, s) {
    var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000];
    var i, j;
    var p = [];
    var q = [];
    for(i=0;i<s;i++) p.push(i);
    for(i=s-1; i>=0; i--) {
        j = Math.floor(n / fact[i]);
        n -= j*fact[i];
        q.push(p[j]);
        for(;j<i;j++) p[j]=p[j+1];
    }
    return q;
}

Now, what you want merely is:

function shuffle(p) {
    var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000];
    return permNth(Math.floor(Math.random()*fact[p.length]), p.length).map(
            function(i) { return p[i]; });
}

It should work for up to 16 elements with a little theoretical bias (though unnoticeable from a practical point of view); it can be seen as fully usable for 15 elements; with arrays containing less than 14 elements, you can safely consider there will be absolutely no bias.

Thomas Baruchel
  • 7,236
  • 2
  • 27
  • 46
3

function shuffleArray(array) {
        // Create a new array with the length of the given array in the parameters
        const newArray = array.map(() => null);

        // Create a new array where each index contain the index value
        const arrayReference = array.map((item, index) => index);

        // Iterate on the array given in the parameters
        array.forEach(randomize);
        
        return newArray;

        function randomize(item) {
            const randomIndex = getRandomIndex();

            // Replace the value in the new array
            newArray[arrayReference[randomIndex]] = item;
            
            // Remove in the array reference the index used
            arrayReference.splice(randomIndex,1);
        }

        // Return a number between 0 and current array reference length
        function getRandomIndex() {
            const min = 0;
            const max = arrayReference.length;
            return Math.floor(Math.random() * (max - min)) + min;
        }
    }
    
console.log(shuffleArray([10,20,30,40,50,60,70,80,90,100]));
Lievno
  • 1,013
  • 9
  • 14
3

Just to have a finger in the pie. Here i present a recursive implementation of Fisher Yates shuffle (i think). It gives uniform randomness.

Note: The ~~ (double tilde operator) is in fact behaves like Math.floor() for positive real numbers. Just a short cut it is.

var shuffle = a => a.length ? a.splice(~~(Math.random()*a.length),1).concat(shuffle(a))
                            : a;

console.log(JSON.stringify(shuffle([0,1,2,3,4,5,6,7,8,9])));

Edit: The above code is O(n^2) due to the employment of .splice() but we can eliminate splice and shuffle in O(n) by the swap trick.

var shuffle = (a, l = a.length, r = ~~(Math.random()*l)) => l ? ([a[r],a[l-1]] = [a[l-1],a[r]], shuffle(a, l-1))
                                                              : a;

var arr = Array.from({length:3000}, (_,i) => i);
console.time("shuffle");
shuffle(arr);
console.timeEnd("shuffle");

The problem is, JS can not coop on with big recursions. In this particular case you array size is limited with like 3000~7000 depending on your browser engine and some unknown facts.

Redu
  • 25,060
  • 6
  • 56
  • 76
3

ES6 compact code using generator function*

This works by randomly removing items from a copy of the unshuffled array until there are none left. It uses the new ES6 generator function.

This will be a perfectly fair shuffle as long as Math.random() is fair.

let arr = [1,2,3,4,5,6,7]

function* shuffle(arr) {
  arr = [...arr];
  while(arr.length) yield arr.splice(Math.random()*arr.length|0, 1)[0]
}

console.log([...shuffle(arr)])

Alternatively, using ES6 and splice:

let arr = [1,2,3,4,5,6,7]

let shuffled = arr.reduce(([a,b])=>
  (b.push(...a.splice(Math.random()*a.length|0, 1)), [a,b]),[[...arr],[]])[1]

console.log(shuffled)

or, ES6 index swap method:

let arr = [1,2,3,4,5,6,7]

let shuffled = arr.reduce((a,c,i,r,j)=>
  (j=Math.random()*(a.length-i)|0,[a[i],a[j]]=[a[j],a[i]],a),[...arr])

console.log(shuffled)
Andrew Parks
  • 6,358
  • 2
  • 12
  • 27
2
var shuffledArray = function(inpArr){
    //inpArr - is input array
    var arrRand = []; //this will give shuffled array
    var arrTempInd = []; // to store shuffled indexes
    var max = inpArr.length;
    var min = 0;
    var tempInd;
    var i = 0;

    do{
        //generate random index between range
        tempInd = Math.floor(Math.random() * (max - min));
        //check if index is already available in array to avoid repetition
        if(arrTempInd.indexOf(tempInd)<0){
            //push character at random index
            arrRand[i] = inpArr[tempInd];
            //push random indexes
            arrTempInd.push(tempInd);
            i++;
        }
    }
    // check if random array length is equal to input array length
    while(arrTempInd.length < max){
        return arrRand; // this will return shuffled Array
    }
};

Just pass the array to function and in return get the shuffled array

Nathan Tuggy
  • 2,237
  • 27
  • 30
  • 38
Mayur Nandane
  • 309
  • 2
  • 8
2

Considering apply it to in loco or to a new immutable array, following other solutions, here is a suggested implementation:

Array.prototype.shuffle = function(local){
  var a = this;
  var newArray = typeof local === "boolean" && local ? this : [];
  for (var i = 0, newIdx, curr, next; i < a.length; i++){
    newIdx = Math.floor(Math.random()*i);
    curr = a[i];
    next = a[newIdx];
    newArray[i] = next;
    newArray[newIdx] = curr;
  }
  return newArray;
};
Andre Pastore
  • 2,841
  • 4
  • 33
  • 44
2

Ronald Fisher and Frank Yates shuffle

ES2015 (ES6) release

Array.prototype.shuffle2 = function () {
    this.forEach(
        function (v, i, a) {
            let j = Math.floor(Math.random() * (i + 1));
            [a[i], a[j]] = [a[j], a[i]];
        }
    );
    return this;
}

Jet optimized ES2015 (ES6) release

Array.prototype.shuffle3 = function () {
    var m = this.length;
    while (m) {
        let i = Math.floor(Math.random() * m--);
        [this[m], this[i]] = [this[i], this[m]];
    }
    return this;
}
SynCap
  • 6,244
  • 2
  • 18
  • 27
  • 3
    This is the same as [BrunoLM's answer](http://stackoverflow.com/a/34377908/1529630) – Oriol Jul 24 '16 at 03:42
  • May be, but I don't thik so: 1) in my examples there are 2 - releases, one - for full understending, second - ready to use optimized version wich 2) not use low level ninja's tricks, 3) works in ANY environment with ES6. Please, don't mess the novices And, hmmm.. BrunoLM updated the answer, and now his anser is more powerful: in BrunoLM's code is not used `Math` but direct low level digital operations, so it's MUCH-MUCH faster. Now (after update) it's works correctly in ANY ES6 envs and strongly recomended, especially for big arrays. Thank u, Oriol. – SynCap Aug 02 '16 at 17:17
2

I see no one has yet given a solution that can be concatenated while not extending the Array prototype (which is a bad practice). Using the slightly lesser known reduce() we can easily do shuffling in a way that allows for concatenation:

var randomsquares = [1, 2, 3, 4, 5, 6, 7].reduce(shuffle).map(n => n*n);

You'd probably want to pass the second parameter [], as otherwise if you try to do this on an empty array it'd fail:

// Both work. The second one wouldn't have worked as the one above
var randomsquares = [1, 2, 3, 4, 5, 6, 7].reduce(shuffle, []).map(n => n*n);
var randomsquares = [].reduce(shuffle, []).map(n => n*n);

Let's define shuffle as:

var shuffle = (rand, one, i, orig) => {
  if (i !== 1) return rand;  // Randomize it only once (arr.length > 1)

  // You could use here other random algorithm if you wanted
  for (let i = orig.length; i; i--) {
    let j = Math.floor(Math.random() * i);
    [orig[i - 1], orig[j]] = [orig[j], orig[i - 1]];
  }

  return orig;
}

You can see it in action in JSFiddle or here:

var shuffle = (all, one, i, orig) => {
    if (i !== 1) return all;

    // You could use here other random algorithm here
    for (let i = orig.length; i; i--) {
        let j = Math.floor(Math.random() * i);
        [orig[i - 1], orig[j]] = [orig[j], orig[i - 1]];
    }

    return orig;
}

for (var i = 0; i < 5; i++) {
  var randomarray = [1, 2, 3, 4, 5, 6, 7].reduce(shuffle, []);
  console.log(JSON.stringify(randomarray));
}
Community
  • 1
  • 1
Francisco Presencia
  • 8,732
  • 6
  • 46
  • 90
  • It seems that you're exchanging for too much times. With `reduce` you can totally perform a [streaming "inside-out" Fisher-Yates](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_.22inside-out.22_algorithm) that uses `(acc, el) => { acc.push(el); let i = Math.floor(Math.random() * (acc.length)); [acc[i], acc[acc.length - 1]] = [acc[acc.length - 1], acc[i]]; return acc; }` as the callback. (Adapted from public domain code on [zhihu](https://www.zhihu.com/question/32303195/answer/127262382).) – Mingye Wang Dec 14 '16 at 04:39
2

I was thinking about oneliner to paste in console. All tricks with .sort was giving wrong results, here is my implementation:

 ['Bob', 'Amy', 'Joy'].map((person) => `${Math.random().toFixed(10)}${person}`).sort().map((person) => person.substr(12));

But don't use it in production code, it's not optimal and works with strings only.

Cezary Daniel Nowak
  • 1,096
  • 13
  • 21
  • It works with any kind of variable: `array.map(e => [Math.random(), e]).sort((a, b) => a[0] - b[0]).map(e => e[1])` (but is not optimal). – Gustavo Rodrigues Aug 15 '17 at 19:43
2
// Create a places array which holds the index for each item in the
// passed in array.
// 
// Then return a new array by randomly selecting items from the
// passed in array by referencing the places array item. Removing that
// places item each time though.
function shuffle(array) {
    let places = array.map((item, index) => index);
    return array.map((item, index, array) => {
      const random_index = Math.floor(Math.random() * places.length);
      const places_value = places[random_index];
      places.splice(random_index, 1);
      return array[places_value];
    })
}
Mudlabs
  • 551
  • 5
  • 16
2

By using shuffle-array module you can shuffle your array . Here is a simple code of it .

var shuffle = require('shuffle-array'),
 //collection = [1,2,3,4,5];
collection = ["a","b","c","d","e"];
shuffle(collection);

console.log(collection);

Hope this helps.

Syed Ayesha Bebe
  • 1,797
  • 1
  • 15
  • 26
2

Edit: Don't use this. The result will always make the elements from the beginning closer to the middle. Who knows, maybe there's a use for this algorithm but not for completely random sorting.

Randomly either push or unshift(add in the beginning).

['a', 'b', 'c', 'd'].reduce((acc, el) => {
  Math.random() > 0.5 ? acc.push(el) : acc.unshift(el);
  return acc;
}, []);
Pawel
  • 16,093
  • 5
  • 70
  • 73
2

For completeness, in addition to the Durstenfeld variation of Fischer-Yates, I'd also point out Sattolo's algorithm which is just one tiny change away and results in every element changing place.

function sattoloCycle(arr) {
   for (let i = arr.length - 1; 0 < i; i--) {
      const j = Math.floor(Math.random() * i);
      [arr[i], arr[j]] = [arr[j], arr[i]];
   }
   return arr
}

The difference is in how random index j is computed, with Math.random() * i versus Math.random() * (i + 1).

Meow
  • 1,610
  • 1
  • 14
  • 18
2

Understandable way of shuffling elements of an array

 let arr1 = ["a", "b", "c", "d"];
 
function shuffle(array){
let currentIndex = array.length;
while(currentIndex !=0){
let randomIndex = Math.floor(Math.random()*array.length);
currentIndex -=1;
let temp = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex]=temp;  
}
return array;
}
let arr2 = shuffle(arr1);
arr2.forEach(element => console.log(element));
twizelissa
  • 111
  • 5
1

This variation of Fisher-Yates is slightly more efficient because it avoids swapping an element with itself:

function shuffle(array) {
  var elementsRemaining = array.length, temp, randomIndex;
  while (elementsRemaining > 1) {
    randomIndex = Math.floor(Math.random() * elementsRemaining--);
    if (randomIndex != elementsRemaining) {
      temp = array[elementsRemaining];
      array[elementsRemaining] = array[randomIndex];
      array[randomIndex] = temp;
    }
  }
  return array;
}
1

Randomize array using array.splice()

function shuffleArray(array) {
   var temp = [];
   var len=array.length;
   while(len){
      temp.push(array.splice(Math.floor(Math.random()*array.length),1)[0]);
      len--;
   }
   return temp;
}
//console.log("Here >>> "+shuffleArray([4,2,3,5,8,1,0]));

demo

Saravanan Rajaraman
  • 1,029
  • 2
  • 12
  • 25
  • 1
    Essentially the same as [Tophe](http://stackoverflow.com/a/18150494/5459839) posted more than a year before. – trincot Jul 20 '16 at 21:08
1

I have written a shuffle function on my own . The difference here is it will never repeat a value (checks in the code for this) :-

function shuffleArray(array) {
 var newArray = [];
 for (var i = 0; i < array.length; i++) {
     newArray.push(-1);
 }

 for (var j = 0; j < array.length; j++) {
    var id = Math.floor((Math.random() * array.length));
    while (newArray[id] !== -1) {
        id = Math.floor((Math.random() * array.length));
    }

    newArray.splice(id, 1, array[j]);
 }
 return newArray; }
Saksham Khurana
  • 872
  • 13
  • 26
1

d3.js provides a built-in version of the Fisher–Yates shuffle:

console.log(d3.shuffle(["a", "b", "c", "d"]));
<script src="http://d3js.org/d3.v5.min.js"></script>

d3.shuffle(array[, lo[, hi]]) <>

Randomizes the order of the specified array using the Fisher–Yates shuffle.

Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
1

Rebuilding the entire array, one by one putting each element at a random place.

[1,2,3].reduce((a,x,i)=>{a.splice(Math.floor(Math.random()*(i+1)),0,x);return a},[])

var ia= [1,2,3];
var it= 1000;
var f = (a,x,i)=>{a.splice(Math.floor(Math.random()*(i+1)),0,x);return a};
var a = new Array(it).fill(ia).map(x=>x.reduce(f,[]));
var r = new Array(ia.length).fill(0).map((x,i)=>a.reduce((i2,x2)=>x2[i]+i2,0)/it)

console.log("These values should be quite equal:",r);
Alex Szücs
  • 561
  • 5
  • 12
  • 3
    You should explain what your code is doing, some people may not understand 1 liners of this complexity. – ricks May 09 '19 at 18:57
  • also note that due to the use of `Math.round(... * i)` this is biased, you want to be doing `Math.floor(.. * (i+1))` instead – Sam Mason May 10 '19 at 08:27
  • @SamMason Probablity of getting .5 is 1:1000000000000000000 – Alex Szücs May 10 '19 at 19:15
  • 1
    if you use `round`, the probability of selecting first and last index (i.e. `0` and `n`) are `0.5/n`, the probability of selecting any other element is `1/n` (where `n = a.length`). this is pretty bad for short arrays – Sam Mason May 10 '19 at 19:27
  • @SamMason thank you for pointing the error, I have updated the answer and made a tester too – Alex Szücs May 10 '19 at 20:08
1
 const arr = [
  { index: 0, value: "0" },
  { index: 1, value: "1" },
  { index: 2, value: "2" },
  { index: 3, value: "3" },
];
let shuffle = (arr) => {
  let set = new Set();
  while (set.size != arr.length) {
    let rand = Math.floor(Math.random() * arr.length);
    set.add(arr[rand]);
  }
  console.log(set);
};
shuffle(arr);
1

For more flexibility you can add another parameter. In this case, you can take a random array from an array and specify the length of the new array:

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

        return array.slice(0, len);
    }
Ali Sharifi Neyestani
  • 4,162
  • 1
  • 14
  • 22
1

I couldn't quite find one that I liked. Here's a solution I came up with. I didn't use too many meaningless variables as this is the way I code now.

Array.prototype.shuffle = function() {
    for (let i in this) {
        if (this.hasOwnProperty(i)) {
            let index = Math.floor(Math.random() * i);
            [
                this[i],
                this[index]
            ] = [
                this[index],
                this[i]
            ];
        }
    }

    return this;
}

let arrayA = [
    "item1", "item2", "item3", "item4", "item5"
];

Array.prototype.shuffle = function() {
    for (let i in this) {
        if (this.hasOwnProperty(i)) {
            let index = Math.floor(Math.random() * i);
            [
                this[i],
                this[index]
            ] = [
                this[index],
                this[i]
            ];
        }
    }
    
    return this;
}

console.log(arrayA.shuffle());

I hope this helps those of you who might not understand this very well.

Calculamatrise
  • 364
  • 3
  • 6
1

Or like all the above answers but in short.

function shuffle(a) { for (var c, d, b = a.length; 0 !== b;)d = Math.floor(Math.random() * b), b -= 1, c = a[b], a[b] = a[d], a[d] = c; return a }
  • why is this all on one line? removing whitespace does not make code 'short' – shea Jan 14 '23 at 03:33
  • It is an optional option, there many reasons why would you need your simple function to be in one line. but for me: it's because I write many codes, and that method seems to put the "shuffle" function in one line, make the whole codes in the page more easier to read. – Abrahem haj hle Jan 14 '23 at 22:26
1

Randomize array without duplicates

    function randomize(array){
        let nums = [];
        for(let i = 0; i < array.length; ++i){
            nums.push(i);
        }   
        nums.sort(() => Math.random() - Math.random()).slice(0, array.length)
        for(let i = 0; i < array.length; ++i){
            array[i] = array[nums[i]];
        }
    }
    randomize(array);
1

Use forEach and Math.random()

var data = ['a','b','c','d','e']
data.forEach( (value,i) => {
   var random = Math.floor(Math.random() * data.length)
   var tmp = data[random]
   data[random] = value
   data[i] = tmp
})
console.log(data)
perona chan
  • 101
  • 1
  • 8
  • I think there is potential for the random number to be the same and so data[random] could contain duplicates. – Sambuxc Aug 22 '23 at 13:39
1

I'll add the solution I use most here because I didn't quite find this approach:

const shuffle = array =>
  array
    // Generate a random number for each elements
    .map(value => [Math.random(), value])

    // Sort using each element random number
    .sort(([a], [b]) => a - b)

    // Return back to an array of values
    .map(entry => entry[1])

What I like about it is the simplicity of the algorithm, assign a random number to each element, then sort.

The swap is a bit harder to visualize I feel and when I have to produce it from nothing I can come up with this one with more confidence, I think it's very clear what it does, and I like that it's not inplace too, most of my suffles are on small arrays so this works great for my usecases.

I still wish we had a shuffle method built-in

kigiri
  • 2,952
  • 21
  • 23
0

Community says arr.sort((a, b) => 0.5 - Math.random()) isn't 100% random!
yes! I tested and recommend do not use this method!

let arr = [1, 2, 3, 4, 5, 6]
arr.sort((a, b) => 0.5 - Math.random());

But I am not sure. So I Write some code to test !...You can also Try ! If you are interested enough!

let data_base = []; 
for (let i = 1; i <= 100; i++) { // push 100 time new rendom arr to data_base!
  data_base.push(
    [1, 2, 3, 4, 5, 6].sort((a, b) => {
      return  Math.random() - 0.5;     // used community banned method!  :-)      
    })
  );
} // console.log(data_base);  // if you want to see data!
let analysis = {};
for (let i = 1; i <= 6; i++) {
  analysis[i] = Array(6).fill(0);
}
for (let num = 0; num < 6; num++) {
  for (let i = 1; i <= 100; i++) {
    let plus = data_base[i - 1][num];
    analysis[`${num + 1}`][plus-1]++;
  }
}
console.log(analysis); // analysed result 

In 100 different random arrays. (my analysed result)

{ player> 1   2   3  4   5   6
   '1': [ 36, 12, 17, 16, 9, 10 ],
   '2': [ 15, 36, 12, 18, 7, 12 ],
   '3': [ 11, 8, 22, 19, 17, 23 ],
   '4': [ 9, 14, 19, 18, 22, 18 ],
   '5': [ 12, 19, 15, 18, 23, 13 ],
   '6': [ 17, 11, 15, 11, 22, 24 ]
}  
// player 1 got > 1(36 times),2(15 times),...,6(17 times)
// ... 
// ...
// player 6 got > 1(10 times),2(12 times),...,6(24 times)

As you can see It is not that much random ! soo... do not use this method!


If you test multiple times.You would see that player 1 got (number 1) so many times!
and player 6 got (number 6) most of the times!
0

Shuffling array using recursion JS.

Not the best implementation but it's recursive and respect immutability.

const randomizer = (array, output = []) => {
    const arrayCopy = [...array];
    if (arrayCopy.length > 0) {    
        const idx = Math.floor(Math.random() * arrayCopy.length);
        const select = arrayCopy.splice(idx, 1);
        output.push(select[0]);
        randomizer(arrayCopy, output);
    }
    return output;
};
Community
  • 1
  • 1
Stiakov
  • 81
  • 1
  • 4
0

I like to share one of the million ways to solve this problem =)

function shuffleArray(array = ["banana", "ovo", "salsicha", "goiaba", "chocolate"]) {
const newArray = [];
let number = Math.floor(Math.random() * array.length);
let count = 1;
newArray.push(array[number]);

while (count < array.length) {
    const newNumber = Math.floor(Math.random() * array.length);
    if (!newArray.includes(array[newNumber])) {
        count++;
        number = newNumber;
        newArray.push(array[number]);
    }
}

return newArray;

}

  • And have you tried this with a million elements? – Scott Sauyet Jan 20 '21 at 16:42
  • I would expect that this is `O (n ^ 2)`. That's why I asked. – Scott Sauyet Jan 22 '21 at 18:08
  • 1
    I made it for a small collection, so I didn't worry about it. The collection I was getting was, for sure, maximum 20 items. good observation! – Bruno de Moraes Feb 19 '21 at 14:44
  • Yes, there's always a question of when to bother with any optimizations. Often, when working with small amounts of data, it's just silly. But several answers here already posted variants of the most common efficient shuffle (Fischer-Yates) and they are not much more complex than this. I'm not suggesting that there's anything wrong here, only that you might want to avoid this for large arrays. – Scott Sauyet Feb 19 '21 at 14:51
0

here with simple while loop

 function ShuffleColor(originalArray) {
        let shuffeledNumbers = [];
        while (shuffeledNumbers.length <= originalArray.length) {
            for (let _ of originalArray) {
                const randomNumb = Math.floor(Math.random() * originalArray.length);
                if (!shuffeledNumbers.includes(originalArray[randomNumb])) {
                    shuffeledNumbers.push(originalArray[randomNumb]);
                }
            }
            if (shuffeledNumbers.length === originalArray.length)
                break;
        }
        return shuffeledNumbers;
    }
const colors = [
    '#000000',
    '#2B8EAD',
    '#333333',
    '#6F98A8',
    '#BFBFBF',
    '#2F454E'
]
ShuffleColor(colors)
-1

Using sort method and Math method :

var arr =  ["HORSE", "TIGER", "DOG", "CAT"];
function shuffleArray(arr){
  return arr.sort( () => Math.floor(Math.random() * Math.floor(3)) - 1)  
}

// every time it gives random sequence
shuffleArr(arr);
// ["DOG", "CAT", "TIGER", "HORSE"]
// ["HORSE", "TIGER", "CAT", "DOG"]
// ["TIGER", "HORSE", "CAT", "DOG"]
Tejas Savaliya
  • 572
  • 7
  • 8
-1
//doesn change array
Array.prototype.shuffle = function () {
    let res = [];
    let copy = [...this];

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

    return res;
};

let a=[1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(a.shuffle());
realmag777
  • 2,050
  • 1
  • 24
  • 22
-2

$=(m)=>console.log(m);

//----add this method to Array class 
Array.prototype.shuffle=function(){
  return this.sort(()=>.5 - Math.random());
};

$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());
Abdennour TOUMI
  • 87,526
  • 38
  • 249
  • 254
  • 2
    This is very bad as the elements have a high probability of staying near their original position or barely moving from there. – Domino Jul 26 '16 at 05:03
  • 2
    If it bad , chain it twice or more : `array.shuffle().shuffle().shuffle()` – Abdennour TOUMI Jul 26 '16 at 06:23
  • 5
    Repeating the call slightly reduces the probabilities of getting very similar results, but it doesn't make it a true random shuffle. In the worst case scenario, even an infinite number of calls to shuffle could still give the exact same array we started with. The Fisher-Yates algorithm is a much better and still efficient choice. – Domino Jul 27 '16 at 04:03
  • 5
    Not the same awful answer again, please. – Oriol Aug 02 '16 at 17:39
-2

A functional solution using Ramda.

const {map, compose, sortBy, prop} = require('ramda')

const shuffle = compose(
  map(prop('v')),
  sortBy(prop('i')),
  map(v => ({v, i: Math.random()}))
)

shuffle([1,2,3,4,5,6,7])
thomas-peter
  • 7,738
  • 6
  • 43
  • 58