Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I shuffle a Javascript Array ensuring each Index is in a new position in the new Array?

I have an Array of Objects, like so.

var usersGoing = [
    { user: 0 },
    { user: 1 },
    { user: 2 },
    { user: 3 },
    { user: 4 }
];

I need to shuffle this Array so that no Object remains in the same index as when it was instantiated, like so:

[
    { user: 3 },
    { user: 2 },
    { user: 4 },
    { user: 0 },
    { user: 1 }
]

It is IMPERATIVE that the resulting array be sorted in this manner, as each of these user Objects will be assigned to a different user Object.

I have tried a few different sorting algorithms, including Fisher-Yates, and I've tried using Underscore.js' _.shuffle(), and this variant from Kirupa Shuffling an Array in JavaScript:

function shuffleFY(input) {
    for (var i = input.length-1; i >=0; i--) {
        var randomIndex = Math.floor(Math.random()*(i+1)); 
        var itemAtIndex = input[randomIndex]; 

        input[randomIndex] = input[i]; 
        input[i] = itemAtIndex;
    }
    return input;
}

Nothing I've tried is working. Help?

UPDATED: I've marked an answer as correct below, as the key points of the Sattolo Cycle were followed correctly. Also, this is NOT a duplicate of Shuffles Random Numbers with no repetition in Javascript/PHP as this question has the additional requirement of the resulting array not only not containing duplicates, but also cannot contain items in their same initial index position.

like image 789
Richard Bennett Avatar asked Nov 09 '15 07:11

Richard Bennett


3 Answers

You posted a link with Sattolo's algorithm in Python:

from random import randrange

def sattoloCycle(items):
    i = len(items)
    while i > 1:
        i = i - 1
        j = randrange(i)  # 0 <= j <= i-1
        items[j], items[i] = items[i], items[j]
    return

Here it is translated to JavaScript:

function sattoloCycle(items) {
  for(var i = items.length; i-- > 1; ) {
    var j = Math.floor(Math.random() * i);
    var tmp = items[i];
    items[i] = items[j];
    items[j] = tmp;
  }
}
like image 145
Shashank Avatar answered Nov 10 '22 11:11

Shashank


For each position, randomly pick an index at a higher position and swap the two.

Each object will either be in its own position and swapped out and have its original position locked, or it will be swapped into a lower position and locked into place by the time its position is up for replacement.

When you get to the second to last position, you will guarantee swap out for the last position (which may or may not be the same value as was originally there), and you're done. Nothing left to do for the final position.

1 2 3 4 5 - original vaues

3 2 1 4 5

3 1 2 4 5

3 1 4 2 5

3 1 4 5 2

like image 44
xaxxon Avatar answered Nov 10 '22 11:11

xaxxon


try this Fisher-Yates-Durstenfeld shuffle:

var usersGoing = [
    { user: 0 },
    { user: 1 },
    { user: 2 },
    { user: 3 },
    { user: 4 }
];


shuffle(usersGoing);
function shuffle(sourceArray) {
    for (var n = 0; n < sourceArray.length - 1; n++) {
        var k = n + Math.floor(Math.random() * (sourceArray.length - n));

        var temp = sourceArray[k];
        sourceArray[k] = sourceArray[n];
        sourceArray[n] = temp;
    }
}

console.log(usersGoing);
like image 37
Suing Avatar answered Nov 10 '22 09:11

Suing