12

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.

Community
  • 1
  • 1
  • 3
    I believe what I'm looking for is a Javascript function representing The Sattolo Cycle [seen here](https://en.wikipedia.org/wiki/Fisher–Yates_shuffle#Sattolo.27s_algorithm). THere's an example in Python there, but I'm Python ignorant and don't know the syntax. – Richard Bennett Nov 09 '15 at 07:35
  • Converting Fisher-Yates-Durstenfeld-Knuth to Sattolo is as simple as changing `Math.random()*(i+1)` to `Math.random()*(i)` – Mac May 27 '18 at 13:16

5 Answers5

4

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;
  }
}
Shashank
  • 13,713
  • 5
  • 37
  • 63
1

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

xaxxon
  • 19,189
  • 5
  • 50
  • 80
1

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);
Suing
  • 465
  • 4
  • 9
0

@xaxxon's deleted answer is right. This is not a meaningful request as per the word "shuffling" is concerned. When you shuffle, you can not set such a limiting condition as shuffling involves randomness. For what you want, just rotate the array and you are good to go. If you are not satisfied with rotation now you have to explain why...

Redu
  • 25,060
  • 6
  • 56
  • 76
-1

Bit outdated answer, probably your software is finished, packed and sold by now but there's better way to achieve this...

const arr = [1,2,3,4,5,6,7,8,9];
const sfl = arr.sort( () => { Math.random() - .5 } );
// sfl == [2,9,5,1,3,6,...]
PRDeving
  • 679
  • 3
  • 11