I have an array like this:
var arr1 = ["a", "b", "c", "d"];
How can I randomize / shuffle it?
I have an array like this:
var arr1 = ["a", "b", "c", "d"];
How can I randomize / shuffle it?
The de-facto unbiased shuffle algorithm is the Fisher–Yates (aka Knuth) Shuffle.
You can see a great visualization here.
function shuffle(array) {
let currentIndex = array.length;
// While there remain elements to shuffle...
while (currentIndex != 0) {
// Pick a remaining element...
let randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
}
// Used like so
let arr = [2, 11, 37, 42];
shuffle(arr);
console.log(arr);
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)
.
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]];
}
}
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. –
Bellerophon 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]]}}
–
Feed 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? –
Recurvate return array;
? –
Darwinism return array
will only allow for easier chaining with other . –
Dermis return array;
is not required, as it is in-place
algorithm, that is, the passed-in array as parameter is changed permanently. –
Wickedness compressed
function slower than normal? does it create temporary 2-element arrays, in each iteration of loop? –
Wickedness 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)
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.
.sort
algorithm –
Pya .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); }; –
Georgeannageorgeanne .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. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… –
Georgeannageorgeanne unshuffled.map(x => [Math.random(), x]).sort().map(([_, x]) => x)
(works because sort() is lexicographical, i.e. looks at first element first) –
Crayton 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.
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();
NEW!
Shorter & probably *faster Fisher-Yates shuffle algorithm
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
}
}
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 –
Washerwoman numberArray(a,b)
with only single parameter numberArray(10)
in your fiddle? –
Brazzaville 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}
–
Remiss function fy(a,b,c=a.length){while(c)b=Math.random()*(--c+1)|0,[a[c],a[b]]=[a[b],a[c]]}
–
Kierakieran 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 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]
)
}
[array[i], array[rand]]=[array[rand], array[i]]
? Maybe you can outline how that works. Why do you choose to iterate downwards? –
Equivalent Edit: This answer is incorrect
See comments and https://mcmap.net/q/45002/-how-to-randomize-shuffle-a-javascript-array. 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>
[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. –
Adamina 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 -
rand
position, i
, in the input array, t
t[i]
to the outputsplice
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 -
i
t[i]
with the last element, t[t.length - 1]
t.pop()
to the resultNow 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;
}
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://mcmap.net/q/45002/-how-to-randomize-shuffle-a-javascript-array.
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);
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:
shuffled
name rather than shuffle
)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] });
}
[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 –
Tuition .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
. –
Radmilla Math.random()
produces. (that is, lexicographic order is the same as numeric order when dealing with numbers from 0 (inclusive) to 1 (exclusive)) –
Tuition .map()
can be simplified to .map(([r, v]) => v )
–
Hickman a.map(r=>[Math.random(),r]).sort().map(r=>r[1])
. This is 6 to 9 times slower than the Fisher-Yates though. –
Godwit 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();
n >>> 0
instead of ~~n
. Array indices can be higher than 2³¹-1. –
Commodore //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.
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;
};
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));
};
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)
Math.random()
function, you will get an uniform distribution (each item has the same chance to be at any position). –
Bacteriostat 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);
}
}
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']
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
copies the initial array
into itemsLeft
picks up a random index from itemsLeft
, adds the corresponding element to the result
array and deletes it from the itemsLeft
array
repeats step (2) until itemsLeft
array gets empty
returns result
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. –
Ire splice
method to create a copy like so: source = array.slice();
. –
Colombo 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.
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://mcmap.net/q/45002/-how-to-randomize-shuffle-a-javascript-array
* https://mcmap.net/q/45002/-how-to-randomize-shuffle-a-javascript-array
*/
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;
};
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));
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.
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%}
reduce
abuse) nor fast (quadratic time). –
Syncretize shift
to O(1) for small (< 1000 element, IIRC) arrays, so it can look like O(n) at small scales. stackblitz.com/edit/shuffler-execution-time-gkgdbl –
Syncretize 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)
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;
-1
for n as you used <
not <=
–
Michalmichalak 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)
"c"
. –
Syncretize 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)
splice
here is to be massively inefficient for no reason. –
Syncretize 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;
}
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);
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.
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]));
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.
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)
.
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;
}
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
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;
};
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));
}
reduce
you can totally perform a streaming "inside-out" Fisher-Yates 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.) –
Veronicaveronika 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.
array.map(e => [Math.random(), e]).sort((a, b) => a[0] - b[0]).map(e => e[1])
(but is not optimal). –
Fabled // 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];
})
}
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.
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.
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;
}, []);
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; }
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);
Math.round(... * i)
this is biased, you want to be doing Math.floor(.. * (i+1))
instead –
Cinnabar 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 –
Cinnabar 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!
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);
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);
}
shuffle
to include a length, but this implementation doesn’t do it and is strictly worse than shuffle(array).slice(0, len)
. –
Syncretize 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);
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)
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;
};
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;
}
O (n ^ 2)
. That's why I asked. –
Bimetallism 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)
This leaves the original array alone.
It builds an array of keys, duplicates a value into a new array using a random key and removes the key from the keys array.
arr = [10,11,12,13,14,15,16,17,18,19,20];
rnd = [];
keys = arr.map((a,b)=>b);
while(keys.length){
rnd.push(arr[keys.splice(Math.floor(Math.random()*keys.length ),1)]);
}
console.log(rnd);
To write out the while loop a bit for clarity:
while(keys.length){
// pick a random position in the keys array
rndkey = Math.floor(Math.random()*keys.length)
//remove a key from the keys array
curkey = keys.splice(rndkey,1)
// use the key to get a value from the array
value = arr[curkey]
// put the value in the new array
rnd.push(value);
}
You often want to shuffle an array often. If it is enormous you could build the keys array one time then .slice() it for each use.
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"]
//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());
$=(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());
array.shuffle().shuffle().shuffle()
–
L 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])
© 2022 - 2024 — McMap. All rights reserved.