Home > OS >  shuffle array without consecetive item - Javascript
shuffle array without consecetive item - Javascript

Time:03-03

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?

CodePudding user response:

You could shuffle until you got a result which has no 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) => Math.abs(v - a[i   1]) === 1))
    };
    indices = [...Array(10).keys()];

shuffle(indices);

console.log(...indices);

CodePudding user response:

You can try this way. It also works if arr will be an array of objects

const arr = [1, 2, 3, 1, 2, 3, 1, 2, 3];

const shuffle = (arr) => {
    const uniqueArr = [...(new Set(arr))];
    const shuffledArr = [];
    let countOfOneObj = 0;

    for (const obj of arr) {
        if (obj === arr[0])   countOfOneObj;
    };

    const getRandomObj = () => {
        const i = Math.floor(Math.random() * uniqueArr.length);
        return uniqueArr[i];
    }

    for (let i = 0; i < countOfOneObj;   i) {

        let usedObjs = []

        for (let j = 0; j < uniqueArr.length;   j) {
            const obj = getRandomObj();

            if (
              usedObjs.includes(obj) ||
              shuffledArr[shuffledArr.length - 1] === obj
            ) {
                --j;
                continue;
            }

            usedObjs.push(obj);
            shuffledArr.push(obj);
        }

        usedObjs = [];
    }

    return shuffledArr;
}

console.log(shuffle(arr));

  • Related