Reena Verma
Reena Verma

Reputation: 1685

Shuffle array automatically 5 times

I have written JavaScript function, to shuffle an array of divs onClick.

  // Function to shuffle 3 divs
  shuffle = () => {
    const shuffled = this.state.divs.sort(() => Math.random() - .50);
    this.setState([...shuffled]);
  };

  // Button as an FYI
  <button onClick={this.shuffle} className="has-text-black">shuffle hats</button>

This works absolutely fine, and randomises every time I click the button.

However, I want the divs to sort/shuffle 5 times automatically, onClick.
(IE = I don't want to have to click 5 times, to shuffle 5 times).

What's the best approach to do this?
(I've searched but haven't found anything to repeat shuffling on elements).

I thought about using async await/settimeout, to repeat this.state.divs.sort(() => Math.random() - .50) 5 times?

UPDATE:

To add context, here is a codesandbox... https://codesandbox.io/s/distracted-shape-ifsiv

When I click the shuffle button, you can see the hats only swap positions once. Not 5 times.

Upvotes: 3

Views: 666

Answers (3)

NVRM
NVRM

Reputation: 13146

Here is a posible way to do it, with javascript.

hats object get shuffled 5 times, with 200ms second transition.

Of course it's very simple, the object is meant to be extended!

let hats = [
  {content: `<h6>1🎩</h6>`},
  {content: `<h3>2🎩</h3>`},
  {content: `<h1>3🎩</h1>`},
  {content: `<h2>4🎩</h2>`},
  {content: `<h4>5🎩</h4>`}
];
let timer;

function loop5(){
  let i = 0
  clearInterval(timer)
  timer = setInterval(function(){
    shuffle()
    if (i >= 5){
      clearInterval(timer)
    } 
    i++    
  }, 200)
}

function shuffle(){
   hats.sort(() => Math.random() - 0.5)
   out.innerHTML = hats.map(e => e.content).join("")
}
div {display: flex; font-size: xx-large }
<button onclick="loop5()">shuffle hats</button>
<div id="out"></div>

Upvotes: 2

Addis
Addis

Reputation: 2530

I think shuffling one time and five times has the same effect, and Fisher-Yates is more efficient but keeping your way:

  shuffle = () => {
    let shuffled = [];
    for(let i=0; i<5; i++) 
      shuffled = this.state.divs.sort(() => Math.random() - .50);
    this.setState([...shuffled]);
  };

If you decide to use "Fisher-Yates" algorithm, you can implement it like:

const shuffle = () => {
    let array = this.state.divs;
    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]];
    }
    this.setState([...array]);
}

As I understood from your comment, you want to do animation, in this case you don't need a loop rather you can use setInterval() and reset it ones executed five times. I have written a demo on both shuffling ways, as you can see the method that uses sort() sometimes returns the same result while the "Fisher–Yates" always reshuffled.

<button onclick="shuffle()">Click To Shuffle</button>
<div id="1">div1</div>
<div id="2">div2</div>
<div id="3">div3</div>
<div id="4">div4</div>

<script>
//This one uses Fisher–Yates shuffle algorithm:
  const divs = [...document.querySelectorAll('div')];

  const shuffle = () => {
    let count = 0;
    const intervalId = setInterval( function() {
      for (let i = divs.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [divs[i], divs[j]] = [divs[j], divs[i]];
      }
      divs.forEach( div => document.body.appendChild(div) );
      count++;
      if(count === 5) 
        clearInterval(intervalId);
    } ,1000)
  }

</script>

<button onclick="shuffle()">Click To Shuffle</button>
<div id="1">div1</div>
<div id="2">div2</div>
<div id="3">div3</div>
<div id="4">div4</div>

<script>

  const divs = [...document.querySelectorAll('div')];

  shuffle = () => {
    let shuffled = [];
    let count = 0;
    const intervalId = setInterval( function() {
      shuffled = divs.sort(() => Math.random() - .50);
      shuffled.forEach( div => document.body.appendChild(div) );
      count++;
      if(count === 5) 
        clearInterval(intervalId);
    }, 1000 )
  };

</script>

For your case, it would be like:

  let divs = this.state.divs;

  const shuffle = () => {
    let count = 0;
    const intervalId = setInterval( function() {
      for (let i = divs.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [divs[i], divs[j]] = [divs[j], divs[i]];
      }
      this.setState([...divs]);
      count++;
      if(count === 5) 
        clearInterval(intervalId);
    } ,1000)
  }

Upvotes: 1

Ismael Padilla
Ismael Padilla

Reputation: 5566

I don't see why shuffling 5 times is better or any different than shuffling 5 times. Anyway you can do it in a naive way like this:

// Function to shuffle 3 divs
  shuffle = () => {
    const shuffled = this.state.divs.sort(() => Math.random() - .50);
    this.setState([...shuffled]);
  };

  shuffleTimesFive = () => {
     for(var i = 0; i < 5; i++) 
         this.shuffle();
  }

  // Button as an FYI
  <button onClick={this.shuffleTimesFive} className="has-text-black">shuffle hats</button>

Or maybe a smarter way is to have a shuffleNTimes function that takes a parameter, like so:

  // Function to shuffle 3 divs
  shuffle = () => {
    const shuffled = this.state.divs.sort(() => Math.random() - .50);
    this.setState([...shuffled]);
  };

  shuffleNTimes = (n) => {
     for(var i = 0; i < n; i++) 
         this.shuffle();
  }

  // Button as an FYI
  <button onClick={this.shuffleNTimes.bind(this, 5)} className="has-text-black">shuffle hats</button>

Upvotes: 1

Related Questions