'shuffle array without consecetive item - Javascript

I currently acheive shuffle one of array without duplicated with previous value of next value.

However, I don't think my method is good. I think there are better way.

Firstly, I will describe what the array looks like.

it's an array of object with property {answer,url,category} and there are duplicated item in the array. think as [A,A,B,B,C,C] , A,B,C as object here.

As EzioMerce pointed out, in this array, object will always has equal amount of numbers. such if there are 3 A, will definitely have 3 B and C. It will not have array as such [A,A,A,B]

I need the array to shuffle until there is no consecutive object next to each other such as [A,B,C,B,A,C]

Here is my solution (which I have tested 40 times without consecutive object):

getShuffleWords(array: Card[]) {
    //First shuffle..
    for (let i = array.length - 1; i > 0; i--) {
      const j = Math.floor( Math.random() * (i - 1));
      if (j > 0) {
        //make sure there are no simliar value around position i and j
        if (array[i]?.answer !== array[j - 1]?.answer
          && array[i]?.answer !== array[j + 1]?.answer
          && array[i]?.answer !== array[j - 1]?.answer
          && array[i]?.answer !== array[j + 1]?.answer) {
          [array[i], array[j]] = [array[j], array[i]];
        }
      } else {
        if (array[i]?.answer !== array[j + 1]?.answer) {
          [array[i], array[j]] = [array[j], array[i]];
        } 
      }
    }

    const before = [...array];
    console.log(before);
    //Checking duplicate and swap with other value
    for (let x = 0; x < array.length; x++){
      if (array[x].answer == array[x + 1]?.answer) {
        console.log(x,array[x])
        for (let y = 0; y < array.length; y++){
          //make sure there are no simliar value around position x and y
          if (array[y]?.answer !== array[x]?.answer
            && array[y + 1]?.answer !== array[x].answer
            && array[y - 1]?.answer !== array[x].answer
            && array[y]?.answer !== array[x+1]?.answer
            && array[y]?.answer !== array[x-1]?.answer
          ) {
            console.log(y, array[y]);
            if (x < y) {
              [array[x], array[y]] = [array[y], array[x]];
            } else {
              [array[y], array[x]] = [array[x], array[y]];
            }
            
            break;
          }
        }
      }
    }

    //just another caculate if there is duplication left (for testing purpose)
    let dup = 0;
    for (let x = 0; x < array.length; x++){
      if (array[x].answer == array[x + 1]?.answer) {
        dup++;
      }
    }
    console.log(array, dup);
    return array;
  }

However, in the //First shuffle.. there are always has some consecutive object exisited in the array. thats why I repeat another checking and replace the value.

How would I improve my method. and why my first shuffle does not work perfectly?



Solution 1:[1]

You could shuffle an array of indices of same grouped values until you got a result which has no same neighbour indices.

const
    fy = array => { // fisher-yates, adapted from https://stackoverflow.com/a/59837259/1447675
        let i = array.length;
        while (--i) {
            const j = Math.floor(Math.random() * (i + 1));
            [array[j], array[i]] = [array[i], array[j]];
        }
    },
    shuffle = array => {
        do fy(array);
        while (array.some((v, i, a) => v === a[i + 1]))
    };
    indices = [0, 0, 0, 1, 1, 1, 2, 2];

shuffle(indices);

console.log(...indices);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1