How to generate non-recurring numbers from the range JavaScript - javascript

I have array with 10 items, I calls one random item using a random number from 1 to 10, what to use to make a random number from 1 to 10, which will not happen again, and when all 10 is used, the program stops randomly? code
const num = () => Math.floor(Math.random() * 10);
const postWord = () => {
randomWord = word.innerText = words[num()].PL;
}
postWord();
submit.addEventListener("click", function() {
postWord();
});

Have you ever considered to move the array items?
var range = 10; // global variable
const num = () => Math.floor(Math.random() * range);
const postWord = () => {
randomWord = word.innerText = words[num()].PL;
for (var i=num(); i < range; i++) {
var temp = words[i];
words[i] = words[i+1];
words[i+1] = temp;
}
range--;
}
postWord();
submit.addEventListener("click", function() {
if (range) {
postWord();
}
});
I am not that familiar with JS but I guess my code can at least demonstrate my point.

Related

Loop function ith timer before if statement

btnRoll.addEventListener ('click', function() {
if (isPlaying) {
diceElement.classList.remove ('hidden');
function changeDiceImage () {
// Display number on the dice
const diceNumber = Math.trunc (Math.random () * 6) + 1;
diceElement.src = `dice${diceNumber}.png`;
i++;
let timer = setTimeout (changeDiceImage, 200);
if (i === 5) {
clearTimeout (timer);
number = diceNumber;
}
timer;
}
let i = 0;
let number = 0;
changeDiceImage ();
if (number !== 1) {
currentScore += number;
document.getElementById (`current--${activePlayer}`).textContent = currentScore;
} else {
switchActivePlayer ();
}
}
})
I try to loop function (that randomly change images with dice) and also last dice number go to number then to score.
In result i receive NaN value instead of number.
How to fix the problem?
First, a couple tools...
A function that returns a promise which resolves after ms...
async function after(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
A function that returns a randomly shuffled array of [1..6], due to Mike Bostock...
function shuffleDie() {
let array = [1,2,3,4,5,6]
let m = 6, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
The OP's aim: present a random sequence of die images and return (a promise that resolves to) the last number in the random sequence...
async function changeDiceImage(diceElement) {
const numbers = shuffleDie();
for (let number of numbers) {
diceElement.src = `dice${number}.png`;
await after(ms);
}
return numbers[5]; // resolve to the last number
}
Use these to clean up the eventListener significantly...
btnRoll.addEventListener ('click', async () => {
if (isPlaying) {
diceElement.classList.remove ('hidden');
let number = await changeDiceImage(diceElement);
if (number !== 1) {
currentScore += number;
document.getElementById (`current--${activePlayer}`).textContent = currentScore;
} else {
switchActivePlayer ();
}
}
})

How to increment in forloop in random iterations?

I'm trying to add a random amount of "boulder" data into my "location" data. I want to use forloop to generate a random amount of boulders to put into each "location". I can't seem to get if I should use forEach or forLoop. In essence I want to know how to generate a forloop that increments in random iterations. (i.e. index=0, randomNum = 5 => nextIndex = 0+5, randomNum = 3, thirdIndex = 5+3 and so on)
here's my randomNum generator
const randomNum = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min)
}
and this is my boulder setting random forLoop (which doesnt work)
const allBoulders = async() => {await Boulder.find().exec()}
for (i=0, i < allBoulders.length; i+=upperCount;) {
console.log(i)
const area = location.area;
const place = location.place;
const latitude = location.latitude;
const longitude = location.longitude;
const boulderLocation = new Location({area: area, place: place, latitude: latitude, longitude: longitude});
const upperCount = indexCount + randomNum(1, 5)
const boulderSet = allBoulders.slice(indexCount, upperCount);
boulderLocation.boulders.push(...boulderSet);
// await boulderLocation.save()
console.log(boulderLocation)
indexCount = upperCount;
};
I'm getting the error:
for (i=0, i < allBoulders.length; i+=upperCount;) {
^
ReferenceError: upperCount is not defined
any help will be greatly appreciated!! thanks in advance!
UPDATE[SOLVED]:
I got it working thanks to answer from swift-lynx
locations.forEach(async(location) => {
const allBoulders = await Boulder.find().exec()
// console.log(foundAllBoulders);
for (let i = 0; i < allBoulders.length;) {
const area = location.area;
const place = location.place;
const latitude = location.latitude;
const longitude = location.longitude;
const boulderLocation = new Location({area: area, place: place, latitude: latitude, longitude: longitude});
const random = randomNum(1, 5);
const upperCount = i + random;
const boulderSet = allBoulders.slice(i, upperCount);
boulderLocation.boulders.push(...boulderSet);
// await boulderLocation.save()
console.log(boulderLocation);
i += random;
}
})
i used a forEach so that i can call the async function, placed a forloop where the randomNum is generated in the loop with i, being iterated from the same randomNum set to const "random".
A loop incrementing randomly, using your randomNum function, can be done like this:
const randomNum = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
// random increments
for (let i = 0; i < 20; i += randomNum(1, 5)) {
console.log(i);
}
If you want to use the random number inside of the for loop, you can do it like this:
// random increments and use random number inside of for loop
for (let i = 0; i < 20;) {
// generate random number
const random = randomNum(1, 5);
// use it as you like
console.log(random);
// increment i manually at the end of the loop
i += randomNum(1, 5);
}
The loop increments in random steps between 1 and 5, not including 5.

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.
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>
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>
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)
}

JavaScript - Random numbers and variables between functions

I am new to JavaScript, I have two roll functions for each roll of a frame. I am unable to get the values of each of those rolls into a frame function to call on and use. If someone could help this would be great! thanks in advance, My code is below.
var Bowling = function() {
var STARTING_TOTAL = 0;
ROLL_ONE = Math.floor(Math.random() * 11);
ROLL_TWO = Math.floor(Math.random() * 11);
this.score = STARTING_TOTAL;
var firstScore;
var secondScore;
var totalScore;
Bowling.prototype.firstRoll = function() {
firstScore = ROLL_ONE
return firstScore;
};
Bowling.prototype.secondRoll = function() {
secondScore = Math.floor(Math.random() * 11 - firstScore);
return secondScore;
};
Bowling.prototype.frameScore = function () {
totalScore = firstScore + secondScore
return totalScore;
};
};
I can only guess what you're trying to achieve. I refactored you code a little bit:
var Bowling = function () {
var STARTING_TOTAL = 0;
this.score = STARTING_TOTAL; // remains unused; did you mean "totalScore"?
this.firstScore = 0;
this.secondScore = 0;
};
Bowling.prototype.firstRoll = function () {
this.firstScore = Math.floor(Math.random() * 11);
return this.firstScore;
};
Bowling.prototype.secondRoll = function () {
this.secondScore = Math.floor(Math.random() * 11 - this.firstScore);
return this.secondScore;
};
Bowling.prototype.frameScore = function () {
this.totalScore = this.firstScore + this.secondScore
return this.totalScore;
};
// now use it like this:
var bowling = new Bowling();
console.log(bowling.firstRoll());
console.log(bowling.secondRoll());
console.log(bowling.frameScore());
In my approach however, firstScore and secondScore are public properties.
To address the question of why the second roll can be negative: as your code currently stands, if the second roll is smaller than the first roll, the result will be negative. If you want it so that if the first roll is 6, the second roll will be a number between 0 and 4, try something like:
function randomInt(maxNum) {
return Math.floor(Math.random() * maxNum)
}
var maxRoll = 11
var rollOne = randomInt(maxRoll)
var rollTwo = randomInt(maxRoll - rollOne)
console.log(rollOne)
console.log(rollTwo)
Press "Run Code Snippet" over and over again to see it work.
Changes I've made:
I made a function, randomInt that gives a random number from 0 to some max number. This saves you from needing to write the same code twice.
I created a variable maxRoll that keeps track of what the highest possible roll is.
I subtract maxRoll from the first roll to determine what the max number for the second roll should be (maxRoll - rollOne). That's then given to randomInt.

Function count calls

I'm a beginner with JavaScript so please be patient =)
I am trying to write a function that counts the number of times it is called. What I have so far is a function with a counter that is incremented explicitly:
var increment = function () {
var i = 0;
this.inc = function () {i += 1;};
this.get = function () {return i;};
};
var ob = new increment();
ob.inc();
ob.inc();
alert(ob.get());
But I'm wondering how to call only ob();, so the function could increment calls made to itself automatically. Is this possible and if so, how?
var increment = function() {
var i = 0;
return function() { return i += 1; };
};
var ob = increment();
ob = function f(){
++f.count || (f.count = 1); // initialize or increment a counter in the function object
return f.count;
}
Wrap a counter to any function:
/**
* Wrap a counter to a function
* Count how many times a function is called
* #param {Function} fn Function to count
* #param {Number} count Counter, default to 1
*/
function addCounterToFn(fn, count = 1) {
return function () {
fn.apply(null, arguments);
return count++;
}
}
See https://jsfiddle.net/n50eszwm/
A one liner option:
const counter = ((count = 0) => () => count++)()
Usage example:
> counter()
0
> counter()
1
> counter()
2
> counter()
3
> counter()
4
> counter()
5
> counter()
6
There are also the new Generator functions, which offer a simple way to write a counter:
function* makeRangeIterator(start = 0, end = 100, step = 1) {
let iterationCount = 0;
for (let i = start; i < end; i += step) {
iterationCount++;
yield i;
}
return iterationCount;
}
const counter = makeRangeIterator();
const nextVal = () => counter.next().value;
console.log("nextVal: ", nextVal()); // 0
console.log("nextVal: ", nextVal()); // 1
console.log("nextVal: ", nextVal()); // 2
console.log("nextVal: ", nextVal()); // 3

Categories