Ali
Ali

Reputation: 267077

How to randomize (shuffle) a JavaScript array?

I have an array like this:

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

How can I randomize / shuffle it?

Upvotes: 2068

Views: 1710479

Answers (30)

Laurens Holst
Laurens Holst

Reputation: 21026

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

Upvotes: 1247

ChristopheD
ChristopheD

Reputation: 116157

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

Upvotes: 2572

user40521
user40521

Reputation: 2119

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.

Upvotes: -1

Evgeniya Manolova
Evgeniya Manolova

Reputation: 2650

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

Upvotes: 8

perona chan
perona chan

Reputation: 191

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)

Upvotes: 0

Pawel
Pawel

Reputation: 18222

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;
}, []);

Upvotes: 2

Andrew Parks
Andrew Parks

Reputation: 8087

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)

Upvotes: 5

Mulan
Mulan

Reputation: 135217

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

Upvotes: 30

Gurami Nikolaishvili
Gurami Nikolaishvili

Reputation: 182

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

Upvotes: 0

Rafi Henig
Rafi Henig

Reputation: 6424

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

Upvotes: 26

Mayank Pathela
Mayank Pathela

Reputation: 648

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)

Upvotes: 6

superluminary
superluminary

Reputation: 49162

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.

Upvotes: 514

Meow
Meow

Reputation: 1742

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).

Upvotes: 2

Ben Carp
Ben Carp

Reputation: 26558

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]
    )
}

Upvotes: 67

Ali Sharifi Neyestani
Ali Sharifi Neyestani

Reputation: 4388

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

Upvotes: 0

user3470138
user3470138

Reputation: 55

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)

Upvotes: -2

Mohamed Salah
Mohamed Salah

Reputation: 49

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

Upvotes: -1

realmag777
realmag777

Reputation: 2088

//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());

Upvotes: -3

Yevhen Horbunkov
Yevhen Horbunkov

Reputation: 15530

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%}

Upvotes: 6

deadrunk
deadrunk

Reputation: 14125

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.

Upvotes: 239

hakki
hakki

Reputation: 6521

//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.

Upvotes: 19

Tejas Savaliya
Tejas Savaliya

Reputation: 638

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

Upvotes: -3

Bruno de Moraes
Bruno de Moraes

Reputation: 79

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;

}

Upvotes: -1

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

Upvotes: 9

Stiakov
Stiakov

Reputation: 89

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

Upvotes: -1

user11748403
user11748403

Reputation:

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!

Upvotes: 0

Redu
Redu

Reputation: 26161

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.

Upvotes: 2

Kris Selbekk
Kris Selbekk

Reputation: 7624

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>

Upvotes: 41

Alex Sz&#252;cs
Alex Sz&#252;cs

Reputation: 681

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

Upvotes: 0

HMR
HMR

Reputation: 39270

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

Upvotes: 3

Related Questions