How do I make a random value not repeat itself? (JavaScript) - javascript

So I want to make it so that every time I run the code the item of this array that shows up will be different. Here is the code I've done so far.
let array = ["apple","orange"];
randomItem = array[Math.floor(Math.random() * array.length)];
if(randomItem != document.getElementById('spaceText').innerHTML){
document.getElementById('spaceText').innerHTML = randomItem;
} else while(randomItem == document.getElementById('spaceText').innerHTML){
randomItem = array[Math.floor(Math.random() * array.length)];
}
I'm not entirely sure how to finish this .. here is how I'm looking at the problem,
You have an array that includes several strings you want to pick from
you want to generate a random number to select one item from the array
you want to check if the selected item is equal to the content of the html element of yours.
if not, then you would like to insert that item into the html element
if it is equal you want to generate another random number

There are multiple ways to achieve this:
You could build a new array without the value you don't want, and choose a random from that.
You could also make a loop which keeps going until you get a value you want.
You could also move the value you don't want to the end of the array, swapping place, and then choose a value within upperBound - 1.
You could also just remove the value you get from the array each time, if you don't want it to appear at all again.
There are multiple ways that all depend on the context and goal of the system.

In your case you don't have to generate a random number more than once, just use filter method
const array = ["Apple","Orange","Cherry","Banana","Pear"];
const spaceText = document.getElementById('spaceText');
function randomText() {
//the next fruit cannot equal the current text content
const options = array.filter(fruit => fruit !== spaceText.textContent);
const randomIndex = Math.floor(Math.random() * options.length);
console.log("Random fruit is:",options[randomIndex]);
spaceText.textContent = options[randomIndex];
}
<div id="spaceText">Apple</div>
<button onclick="randomText()">Randomize</button>

Does this help you? All it does is selecting a new item from the array and taking care that it is not the one that was selected previously
const items = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"];
let selectedItemIndex;
function selectNewOne() {
const randomItemIndex = Math.floor(Math.random() * items.length);
if (randomItemIndex === selectedItemIndex) {
return selectNewOne();
}
return randomItemIndex;
}
// this is just for testing purposes
for (let i = 0; i < 5; i++) {
selectedItemIndex = selectNewOne();
console.log(items[selectedItemIndex]);
}

Here is one approach, Keep the random number in object (or set) and generate new random until you found not already seen one. Generator function will be helpful to do that.
const getRandomUniqueInList = function* (arr) {
const myRandom = () => Math.floor((Math.random() * 100) % arr.length);
const list = [];
while (list.length < arr.length) {
let index = myRandom();
while (list.includes(index)) {
index = myRandom();
}
list.push(index);
yield index;
}
return -1;
};
const items = new Array(10).fill(0);
const itr = getRandomUniqueInList(items);
let next = itr.next();
while (!next.done) {
console.log('Random: ', next.value);
next = itr.next();
}
Another variant for get random upto specified max number and implement with Set and generator method.
const getRandomUpto = function* (max = 50) {
const myRandom = () => Math.ceil(Math.random() * max);
const set = new Set();
let rand = myRandom();
while (set.size < max) {
while (set.has(rand)) {
rand = myRandom();
}
set.add(rand);
yield rand;
}
};
const sample_max = 10;
const itr = getRandomUpto(sample_max);
let next = itr.next();
while(!next.done) {
const myRandom = () => Math.ceil(Math.random() * sample_max);
console.log('Random, (not repeat):', next.value, '(may repeat): ', myRandom());
next = itr.next()
}

Based on your code sample. You are using innerHTML which assumed to be string. So the approach is when you trigger the function, pull a random element/word from the array, then matches if it exists in the current innerHTML value, if it is true, it will skip it else it will append the random word into the innerHTML.
This could work with arrays as well as an alternative to innerHTML / string
let array = ["apple","orange", "banana","cherry","strawberry" ];
random = () =>{
let randomItem = array[Math.floor(Math.random() * array.length)];
let currentString = document.getElementById('spaceText').innerHTML
if( ! (parseFloat(currentString.indexOf(randomItem)) >=0) ){
currentString += ' '+randomItem
document.getElementById('spaceText').innerHTML = currentString
}else{
console.log(randomItem+' already exists')
}
}
<div id="spaceText">Inital value</div>
<button onclick="random()">Random</button>

Related

How to prevent Math.random from repeating a return? [duplicate]

I have the following function
function randomNum(max, used){
newNum = Math.floor(Math.random() * max + 1);
if($.inArray(newNum, used) === -1){
console.log(newNum + " is not in array");
return newNum;
}else{
return randomNum(max,used);
}
}
Basically I am creating a random number between 1 - 10 and checking to see if that number has already been created, by adding it to an array and checking the new created number against it. I call it by adding it to a variable..
UPDATED:
for(var i=0;i < 10;i++){
randNum = randomNum(10, usedNums);
usedNums.push(randNum);
//do something with ranNum
}
This works, but in Chrome I get the following error:
Uncaught RangeError: Maximum call stack size exceeded
Which I guess it's because I am calling the function inside itself too many times. Which means my code is no good.
Can someone help me with the logic? what's a best way to make sure my numbers are not repeating?
If I understand right then you're just looking for a permutation (i.e. the numbers randomised with no repeats) of the numbers 1-10?
Maybe try generating a randomised list of those numbers, once, at the start, and then just working your way through those?
This will calculate a random permutation of the numbers in nums:
var nums = [1,2,3,4,5,6,7,8,9,10],
ranNums = [],
i = nums.length,
j = 0;
while (i--) {
j = Math.floor(Math.random() * (i+1));
ranNums.push(nums[j]);
nums.splice(j,1);
}
So, for example, if you were looking for random numbers between 1 - 20 that were also even, then you could use:
nums = [2,4,6,8,10,12,14,16,18,20];
Then just read through ranNums in order to recall the random numbers.
This runs no risk of it taking increasingly longer to find unused numbers, as you were finding in your approach.
EDIT: After reading this and running a test on jsperf, it seems like a much better way of doing this is a Fisher–Yates Shuffle:
function shuffle(array) {
var i = array.length,
j = 0,
temp;
while (i--) {
j = Math.floor(Math.random() * (i+1));
// swap randomly chosen element with current element
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
var ranNums = shuffle([1,2,3,4,5,6,7,8,9,10]);
Basically, it's more efficient by avoiding the use of 'expensive' array operations.
BONUS EDIT: Another possibility is using generators (assuming you have support):
function* shuffle(array) {
var i = array.length;
while (i--) {
yield array.splice(Math.floor(Math.random() * (i+1)), 1)[0];
}
}
Then to use:
var ranNums = shuffle([1,2,3,4,5,6,7,8,9,10]);
ranNums.next().value; // first random number from array
ranNums.next().value; // second random number from array
ranNums.next().value; // etc.
where ranNums.next().value will eventually evaluate to undefined once you've run through all the elements in the shuffled array.
Overall this won't be as efficient as the Fisher–Yates Shuffle because you're still splice-ing an array. But the difference is that you're now doing that work only when you need it rather than doing it all upfront, so depending upon your use case, this might be better.
//random number without repetition in JavaScript, Just in one line;
//it can be used as _id;
//it not need to store or check;
const myRnId = () => parseInt(Date.now() * Math.random());
console.log(myRnId()); // any random number included timeStamp;
function Myrand(max,min){
arr=[];
for (i = 0; i < max; i++) {
x = Math.floor( Math.random() * max) + min;
if(arr.includes(x) == true){
i=i-1;
}else{
if(x>max==false){
arr.push(x);
}
}
}
return arr;
}
console.log(Myrand(5,1));
Try this:
var numbers = []; // new empty array
var min, max, r, n, p;
min = 1;
max = 50;
r = 5; // how many numbers you want to extract
for (let i = 0; i < r; i++) {
do {
n = Math.floor(Math.random() * (max - min + 1)) + min;
p = numbers.includes(n);
if(!p){
numbers.push(n);
}
}
while(p);
}
console.log(numbers.join(" - "));
let arr = [];
do {
let num = Math.floor(Math.random() * 10 + 1);
arr.push(num);
arr = arr.filter((item, index) => {
return arr.indexOf(item) === index
});
} while (arr.length < 10);
console.log(arr);
HTML
<p id="array_number" style="font-size: 25px; text-align: center;"></p>
JS
var min = 1;
var max = 90;
var stop = 6; //Number of numbers to extract
var numbers = [];
for (let i = 0; i < stop; i++) {
var n = Math.floor(Math.random() * max) + min;
var check = numbers.includes(n);
if(check === false) {
numbers.push(n);
} else {
while(check === true){
n = Math.floor(Math.random() * max) + min;
check = numbers.includes(n);
if(check === false){
numbers.push(n);
}
}
}
}
sort();
//Sort the array in ascending order
function sort() {
numbers.sort(function(a, b){return a-b});
document.getElementById("array_number").innerHTML = numbers.join(" - ");
}
DEMO
The issue is that as you approach saturation you begin to take longer and longer to generate a unique number "randomly". For instance, in the example you provided above the max is 10. Once the used number array contains 8 numbers it can potentially take a long time for the 9th and 10th to be found. This is probably where the maximum call stack error is being generated.
jsFiddle Demo showing iteration count being maxed
By iterating inside of your recursion, you can see that a large amount of execution occurs when the array is completely saturated, but the function is called. In this scenario, the function should exit.
jsFiddle Demo with early break
if( used.length >= max ) return undefined;
And one last way to accomplish both the iteration checks and the infinite recursion would be like this jsFiddle Demo:
function randomNum(max, used, calls){
if( calls == void 0 ) calls = 0;
if( calls++ > 10000 ) return undefined;
if( used.length >= max ) return undefined;
var newNum = Math.floor(Math.random() * max + 1);
if($.inArray(newNum, used) === -1){
return newNum;
}else{
return randomNum(max,used,calls);
}
}
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript Math.random()</h2>
<p>Math.random() returns a random number between 0 (included) and 1 (excluded):</p>
<p id="demo"></p>
<script>
var storeArray = []
function callRamdom(){
var randomNumber = Math.floor(Math.random() * 5);
return randomNumber;
}
function randomStore(){
var localValue = callRamdom()
var status = false;
for(i=0;i<5; i++){
var aa = storeArray[i];
if(aa!=localValue){
console.log(storeArray[i]+"hhhhh"+ localValue);
if(i==4){
status=true;
}
}
else
break;
}
if(status==true){
storeArray.push(localValue);
}
if(storeArray.length!=5){
randomStore();
}
return storeArray;
}
document.getElementById("demo").innerHTML = randomStore();
</script>
</body>
</html>
while(randArr.length < SIZEOFARRAY){
val = Math.floor((Math.random() * RANGEOFVALUES));
if(randArr.indexOf(val) < 0){
randArr.push(val);
}
}
You can change SIZEOFARRAY to the size of the array you wish to use
and also change RANGEOFVALUES to the range of values you wish to randomize
const GenerateRandomNumbers = (max) => {
let orderNumbers = new Set();
for (let i = 1; ;i++){
let random = Math.floor(Math.random() * max + 1) ;
orderNumbers.add(random);
if (orderNumbers.size == max){
break;
}
}
return orderNumbers;}
function randomNumbers(max) {
function range(upTo) {
var result = [];
for(var i = 0; i < upTo; i++) result.push(i);
return result;
}
function shuffle(o){
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
}
var myArr = shuffle(range(max));
return function() {
return myArr.shift();
};
}
Built a little test, try this on jsfiddle:
var randoms = randomNumbers(10),
rand = randoms(),
result = [];
while(rand != null) {
result.push(rand);
rand = randoms();
}
console.log(result);
Shuffle function courtesy of dzone.com.
This is how I achieve it using underscore.js
To get n integers from min to max values. Where n is the size argument.
var randomNonRepeatingIntFromInterval = function(min, max, size) {
var values = [];
while (values.length < size) {
values.push(Math.floor(Math.random() * ( max - min + 1) + min));
values = _.uniq(values);
}
return values;
}
Sorry this is a new answer to an old question, but this can be done more efficiently with a map. What you're after is random selection rather than non-repeating random. Non-repeating random is nonsensical.
Where _a is the collection, and r is not part of the collection, we lambda the random value r:
function aRandom(f){
var r = Math.random();
aRandom._a[r] ? aRandom(f) : f(r,aRandom._a[r] = 1);
}
aRandom._a = {};
//usage:
aRandom(function(r){ console.log(r) });
Redefine aRandom._a when the browser gets sluggish. To avoid eventual sluggishness, one should really use an UUID generation algo with sufficient entropy so that chances of repeat are effectively zero, rather than brute forcing differentiability. I chose the function name aRandom because latin prefix A- means "away from." Since the more it's used, the further away from random the output. The function produces one million unique values in 2100 ms on a Macbook.
Advantage of the above solution is no need to limit the set. As well, multiple callers can use it at the same time and assume their values are different from all other callers. This is handy for such things as noise jittering distributions with insured no overlaps.
However, it can be modified to return integers as well, so as to restrict ram use to the length supplied:
function aRandom(f,c){
var r = Math.floor(Math.random()*c);
aRandom._a[r] ? aRandom(f,c) : f(r,aRandom._a[r] = 1);
}
aRandom._a = {};
//usage:
var len = 10;
var resultset = [];
for(var i =0; i< len; i++){
aRandom(function(r){ resultset.push(r); }, len);
}
console.log(resultset);
randojs.com makes this a simple one-liner:
randoSequence(1, 10)
This will return an array of numbers from 1 through 10 in random order.
You just need to add the following to the head of your html document, and you can do pretty much whatever you want with randomness easily. Random values from arrays, random jquery elements, random properties from objects, and even preventing repetitions as I've shown here.
<script src="https://randojs.com/1.0.0.js"></script>
Just one solution for reference
const fiveNums = () => {
const ranNum = () => Math.floor(Math.random() * (10 + 1));
let current;
let arr = [];
while(arr.length < 5) {
if(arr.indexOf(current = ranNum()) === -1) {
arr.push(current);
}
}
return arr;
};
fiveNums();
In case no permutation is wanted and/or length shall be variable, here is a solution for non repeating randomized lists/arrays without if-statements:
Shuffle function:
Input:
Array or object(list) of arbitrary length
optional: last key to be filtered (Array: index number, List: String of key)
Output:
random Key
to get your random item use myArrayOrList[key]
// no repeat if old_key is provided
function myShuffle(arr_or_list, old_key = false) {
var keys = Array.from(Object.keys(arr_or_list)); //extracts keys
if (old_key != false) {
keys.splice(keys.indexOf(old_key), 1); // removes old_key from keys
};
var randomKey = keys[Math.floor(Math.random() * keys.length)]; // get random key
return randomKey;
}
//test:
const a = [10, 20, 30, 40, 50, 60];
const b = {
"a": 10,
"bla-bla bla": 20,
"d": 30,
"c": 40
};
var oldKeys_a = [];
var oldKeys_b = [];
oldKeys_a[0] = myShuffle(a);
oldKeys_b[0] = myShuffle(b);
var i;
for (i = 1; i < 10; i++) {
oldKeys_a[i] = myShuffle(a, oldKeys_a[i - 1]);
oldKeys_b[i] = myShuffle(b, oldKeys_b[i - 1]);
}
alert('oldKeys_a: ' + oldKeys_a + '; oldKeys_b: ' + oldKeys_b)
//random...
//>>> oldKeys_a: 1,3,0,0,5,0,4,5,2,3; oldKeys_b: d,a,d,bla-bla bla,a,c,d,bla-bla bla,a,d <<<
Non-repeating range random number generation with the recursive patterns.
const getRandom = (max, memo) => {
if (max != memo.length) {
const pos = Math.floor(Math.random() * max);
if (memo.includes(pos)) {
return getRandom(max, memo);
} else {
return pos;
}
}
}
const random = [];
const range = 6;
for (let index = 0; index < range; index++) {
random.push(getRandom(range, random))
}
console.log('random', random) // random (6) [5, 3, 0, 2, 1, 4]
function getRandomNumberNoRepeat(length){
let numberPick = [0,1,2,3,4,5,6,7,8,9]
return numberPick.sort(() => Math.random() -0.5).slice(0, length)}
console.log(getRandomNumberNoRepeat(3));
let display = document.getElementById("container");
let myArray = [];
let randomiser = (min, max, vals) => {
while (myArray.length < vals) {
let randNum = Math.floor(Math.random() * (max - min + 1) + min);
if (!myArray.includes(randNum)) {
myArray.push(randNum);
}
}
return (display.textContent = myArray.join(" - "));
};
randomiser(1, 35, 7);
Here is one where you can specify how many number you need.
I decided to write this code and make a chrome extension for it to use when playing the lotto. haha. Just a learning experience for me and thanks to all who contributed. I read all posts and I'm better of today than I was yesterday in understanding shuffle and random non repeating numbers.
You don't really want a lost of random numbers. Truly random numbers must be able to repeat.
Truly random number are like throwing dice. Any number can come up next.
Shuffled numbers are like drawing playing cards. Each number can come up only once.
What you are really asking for is to shuffle a list of numbers and then use the first so many numbers from the shuffled list.
Think of making a list of numbers in order, and then using the random number generator to randomly select a number from a copy of that list. Each time, put the selected number at the end of your new list and remove it from the copy of the old list, shortening that list. When you are done, the new list will contain the shuffled numbers and the copy of the old list will be empty.
Alternately, you can take the number selected and use it immediately, shortening the copy of the list by removing the used number. Because you have removed the number from the list, it can't come up again.

Get Random unique variable num non-repeating previous or next In javascript [duplicate]

I have the following function
function randomNum(max, used){
newNum = Math.floor(Math.random() * max + 1);
if($.inArray(newNum, used) === -1){
console.log(newNum + " is not in array");
return newNum;
}else{
return randomNum(max,used);
}
}
Basically I am creating a random number between 1 - 10 and checking to see if that number has already been created, by adding it to an array and checking the new created number against it. I call it by adding it to a variable..
UPDATED:
for(var i=0;i < 10;i++){
randNum = randomNum(10, usedNums);
usedNums.push(randNum);
//do something with ranNum
}
This works, but in Chrome I get the following error:
Uncaught RangeError: Maximum call stack size exceeded
Which I guess it's because I am calling the function inside itself too many times. Which means my code is no good.
Can someone help me with the logic? what's a best way to make sure my numbers are not repeating?
If I understand right then you're just looking for a permutation (i.e. the numbers randomised with no repeats) of the numbers 1-10?
Maybe try generating a randomised list of those numbers, once, at the start, and then just working your way through those?
This will calculate a random permutation of the numbers in nums:
var nums = [1,2,3,4,5,6,7,8,9,10],
ranNums = [],
i = nums.length,
j = 0;
while (i--) {
j = Math.floor(Math.random() * (i+1));
ranNums.push(nums[j]);
nums.splice(j,1);
}
So, for example, if you were looking for random numbers between 1 - 20 that were also even, then you could use:
nums = [2,4,6,8,10,12,14,16,18,20];
Then just read through ranNums in order to recall the random numbers.
This runs no risk of it taking increasingly longer to find unused numbers, as you were finding in your approach.
EDIT: After reading this and running a test on jsperf, it seems like a much better way of doing this is a Fisher–Yates Shuffle:
function shuffle(array) {
var i = array.length,
j = 0,
temp;
while (i--) {
j = Math.floor(Math.random() * (i+1));
// swap randomly chosen element with current element
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
var ranNums = shuffle([1,2,3,4,5,6,7,8,9,10]);
Basically, it's more efficient by avoiding the use of 'expensive' array operations.
BONUS EDIT: Another possibility is using generators (assuming you have support):
function* shuffle(array) {
var i = array.length;
while (i--) {
yield array.splice(Math.floor(Math.random() * (i+1)), 1)[0];
}
}
Then to use:
var ranNums = shuffle([1,2,3,4,5,6,7,8,9,10]);
ranNums.next().value; // first random number from array
ranNums.next().value; // second random number from array
ranNums.next().value; // etc.
where ranNums.next().value will eventually evaluate to undefined once you've run through all the elements in the shuffled array.
Overall this won't be as efficient as the Fisher–Yates Shuffle because you're still splice-ing an array. But the difference is that you're now doing that work only when you need it rather than doing it all upfront, so depending upon your use case, this might be better.
//random number without repetition in JavaScript, Just in one line;
//it can be used as _id;
//it not need to store or check;
const myRnId = () => parseInt(Date.now() * Math.random());
console.log(myRnId()); // any random number included timeStamp;
function Myrand(max,min){
arr=[];
for (i = 0; i < max; i++) {
x = Math.floor( Math.random() * max) + min;
if(arr.includes(x) == true){
i=i-1;
}else{
if(x>max==false){
arr.push(x);
}
}
}
return arr;
}
console.log(Myrand(5,1));
Try this:
var numbers = []; // new empty array
var min, max, r, n, p;
min = 1;
max = 50;
r = 5; // how many numbers you want to extract
for (let i = 0; i < r; i++) {
do {
n = Math.floor(Math.random() * (max - min + 1)) + min;
p = numbers.includes(n);
if(!p){
numbers.push(n);
}
}
while(p);
}
console.log(numbers.join(" - "));
let arr = [];
do {
let num = Math.floor(Math.random() * 10 + 1);
arr.push(num);
arr = arr.filter((item, index) => {
return arr.indexOf(item) === index
});
} while (arr.length < 10);
console.log(arr);
HTML
<p id="array_number" style="font-size: 25px; text-align: center;"></p>
JS
var min = 1;
var max = 90;
var stop = 6; //Number of numbers to extract
var numbers = [];
for (let i = 0; i < stop; i++) {
var n = Math.floor(Math.random() * max) + min;
var check = numbers.includes(n);
if(check === false) {
numbers.push(n);
} else {
while(check === true){
n = Math.floor(Math.random() * max) + min;
check = numbers.includes(n);
if(check === false){
numbers.push(n);
}
}
}
}
sort();
//Sort the array in ascending order
function sort() {
numbers.sort(function(a, b){return a-b});
document.getElementById("array_number").innerHTML = numbers.join(" - ");
}
DEMO
The issue is that as you approach saturation you begin to take longer and longer to generate a unique number "randomly". For instance, in the example you provided above the max is 10. Once the used number array contains 8 numbers it can potentially take a long time for the 9th and 10th to be found. This is probably where the maximum call stack error is being generated.
jsFiddle Demo showing iteration count being maxed
By iterating inside of your recursion, you can see that a large amount of execution occurs when the array is completely saturated, but the function is called. In this scenario, the function should exit.
jsFiddle Demo with early break
if( used.length >= max ) return undefined;
And one last way to accomplish both the iteration checks and the infinite recursion would be like this jsFiddle Demo:
function randomNum(max, used, calls){
if( calls == void 0 ) calls = 0;
if( calls++ > 10000 ) return undefined;
if( used.length >= max ) return undefined;
var newNum = Math.floor(Math.random() * max + 1);
if($.inArray(newNum, used) === -1){
return newNum;
}else{
return randomNum(max,used,calls);
}
}
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript Math.random()</h2>
<p>Math.random() returns a random number between 0 (included) and 1 (excluded):</p>
<p id="demo"></p>
<script>
var storeArray = []
function callRamdom(){
var randomNumber = Math.floor(Math.random() * 5);
return randomNumber;
}
function randomStore(){
var localValue = callRamdom()
var status = false;
for(i=0;i<5; i++){
var aa = storeArray[i];
if(aa!=localValue){
console.log(storeArray[i]+"hhhhh"+ localValue);
if(i==4){
status=true;
}
}
else
break;
}
if(status==true){
storeArray.push(localValue);
}
if(storeArray.length!=5){
randomStore();
}
return storeArray;
}
document.getElementById("demo").innerHTML = randomStore();
</script>
</body>
</html>
while(randArr.length < SIZEOFARRAY){
val = Math.floor((Math.random() * RANGEOFVALUES));
if(randArr.indexOf(val) < 0){
randArr.push(val);
}
}
You can change SIZEOFARRAY to the size of the array you wish to use
and also change RANGEOFVALUES to the range of values you wish to randomize
const GenerateRandomNumbers = (max) => {
let orderNumbers = new Set();
for (let i = 1; ;i++){
let random = Math.floor(Math.random() * max + 1) ;
orderNumbers.add(random);
if (orderNumbers.size == max){
break;
}
}
return orderNumbers;}
function randomNumbers(max) {
function range(upTo) {
var result = [];
for(var i = 0; i < upTo; i++) result.push(i);
return result;
}
function shuffle(o){
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
}
var myArr = shuffle(range(max));
return function() {
return myArr.shift();
};
}
Built a little test, try this on jsfiddle:
var randoms = randomNumbers(10),
rand = randoms(),
result = [];
while(rand != null) {
result.push(rand);
rand = randoms();
}
console.log(result);
Shuffle function courtesy of dzone.com.
This is how I achieve it using underscore.js
To get n integers from min to max values. Where n is the size argument.
var randomNonRepeatingIntFromInterval = function(min, max, size) {
var values = [];
while (values.length < size) {
values.push(Math.floor(Math.random() * ( max - min + 1) + min));
values = _.uniq(values);
}
return values;
}
Sorry this is a new answer to an old question, but this can be done more efficiently with a map. What you're after is random selection rather than non-repeating random. Non-repeating random is nonsensical.
Where _a is the collection, and r is not part of the collection, we lambda the random value r:
function aRandom(f){
var r = Math.random();
aRandom._a[r] ? aRandom(f) : f(r,aRandom._a[r] = 1);
}
aRandom._a = {};
//usage:
aRandom(function(r){ console.log(r) });
Redefine aRandom._a when the browser gets sluggish. To avoid eventual sluggishness, one should really use an UUID generation algo with sufficient entropy so that chances of repeat are effectively zero, rather than brute forcing differentiability. I chose the function name aRandom because latin prefix A- means "away from." Since the more it's used, the further away from random the output. The function produces one million unique values in 2100 ms on a Macbook.
Advantage of the above solution is no need to limit the set. As well, multiple callers can use it at the same time and assume their values are different from all other callers. This is handy for such things as noise jittering distributions with insured no overlaps.
However, it can be modified to return integers as well, so as to restrict ram use to the length supplied:
function aRandom(f,c){
var r = Math.floor(Math.random()*c);
aRandom._a[r] ? aRandom(f,c) : f(r,aRandom._a[r] = 1);
}
aRandom._a = {};
//usage:
var len = 10;
var resultset = [];
for(var i =0; i< len; i++){
aRandom(function(r){ resultset.push(r); }, len);
}
console.log(resultset);
randojs.com makes this a simple one-liner:
randoSequence(1, 10)
This will return an array of numbers from 1 through 10 in random order.
You just need to add the following to the head of your html document, and you can do pretty much whatever you want with randomness easily. Random values from arrays, random jquery elements, random properties from objects, and even preventing repetitions as I've shown here.
<script src="https://randojs.com/1.0.0.js"></script>
Just one solution for reference
const fiveNums = () => {
const ranNum = () => Math.floor(Math.random() * (10 + 1));
let current;
let arr = [];
while(arr.length < 5) {
if(arr.indexOf(current = ranNum()) === -1) {
arr.push(current);
}
}
return arr;
};
fiveNums();
In case no permutation is wanted and/or length shall be variable, here is a solution for non repeating randomized lists/arrays without if-statements:
Shuffle function:
Input:
Array or object(list) of arbitrary length
optional: last key to be filtered (Array: index number, List: String of key)
Output:
random Key
to get your random item use myArrayOrList[key]
// no repeat if old_key is provided
function myShuffle(arr_or_list, old_key = false) {
var keys = Array.from(Object.keys(arr_or_list)); //extracts keys
if (old_key != false) {
keys.splice(keys.indexOf(old_key), 1); // removes old_key from keys
};
var randomKey = keys[Math.floor(Math.random() * keys.length)]; // get random key
return randomKey;
}
//test:
const a = [10, 20, 30, 40, 50, 60];
const b = {
"a": 10,
"bla-bla bla": 20,
"d": 30,
"c": 40
};
var oldKeys_a = [];
var oldKeys_b = [];
oldKeys_a[0] = myShuffle(a);
oldKeys_b[0] = myShuffle(b);
var i;
for (i = 1; i < 10; i++) {
oldKeys_a[i] = myShuffle(a, oldKeys_a[i - 1]);
oldKeys_b[i] = myShuffle(b, oldKeys_b[i - 1]);
}
alert('oldKeys_a: ' + oldKeys_a + '; oldKeys_b: ' + oldKeys_b)
//random...
//>>> oldKeys_a: 1,3,0,0,5,0,4,5,2,3; oldKeys_b: d,a,d,bla-bla bla,a,c,d,bla-bla bla,a,d <<<
Non-repeating range random number generation with the recursive patterns.
const getRandom = (max, memo) => {
if (max != memo.length) {
const pos = Math.floor(Math.random() * max);
if (memo.includes(pos)) {
return getRandom(max, memo);
} else {
return pos;
}
}
}
const random = [];
const range = 6;
for (let index = 0; index < range; index++) {
random.push(getRandom(range, random))
}
console.log('random', random) // random (6) [5, 3, 0, 2, 1, 4]
function getRandomNumberNoRepeat(length){
let numberPick = [0,1,2,3,4,5,6,7,8,9]
return numberPick.sort(() => Math.random() -0.5).slice(0, length)}
console.log(getRandomNumberNoRepeat(3));
let display = document.getElementById("container");
let myArray = [];
let randomiser = (min, max, vals) => {
while (myArray.length < vals) {
let randNum = Math.floor(Math.random() * (max - min + 1) + min);
if (!myArray.includes(randNum)) {
myArray.push(randNum);
}
}
return (display.textContent = myArray.join(" - "));
};
randomiser(1, 35, 7);
Here is one where you can specify how many number you need.
I decided to write this code and make a chrome extension for it to use when playing the lotto. haha. Just a learning experience for me and thanks to all who contributed. I read all posts and I'm better of today than I was yesterday in understanding shuffle and random non repeating numbers.
You don't really want a lost of random numbers. Truly random numbers must be able to repeat.
Truly random number are like throwing dice. Any number can come up next.
Shuffled numbers are like drawing playing cards. Each number can come up only once.
What you are really asking for is to shuffle a list of numbers and then use the first so many numbers from the shuffled list.
Think of making a list of numbers in order, and then using the random number generator to randomly select a number from a copy of that list. Each time, put the selected number at the end of your new list and remove it from the copy of the old list, shortening that list. When you are done, the new list will contain the shuffled numbers and the copy of the old list will be empty.
Alternately, you can take the number selected and use it immediately, shortening the copy of the list by removing the used number. Because you have removed the number from the list, it can't come up again.

Why is the textContent not being replaced but rather added to when function is clicked?

const passwordBoxLeft = document.getElementById('password-box--left');
const passwordBoxRight = document.getElementById('password-box--right');
let passwordLeft = [];
let passwordRight = [];
let passwordLength = 15;
function generatePassword() {
for (let i = 0; i < passwordLength; i++) {
let leftPW = characters[Math.floor(Math.random() * characters.length)];
let rightPW = characters[Math.floor(Math.random() * characters.length)];
passwordLeft.push(leftPW);
passwordRight.push(rightPW);
};
passwordBoxLeft.textContent = passwordLeft.join('');
passwordBoxRight.textContent = passwordRight.join('');
};
I have a simple frontend password generator that takes a characters array and generates 15 random characters from said array, pushes this into container arrays and these arrays are then displayed as textContent on the DOM.
Everything works well but for some reason, I am not understanding how to replace the textContent divs with a new rendition of a random password. Instead, the function is acting as if I have placed a += for textContent so it keeps joining on another 15 random characters and so on everytime it is clicked, rather than showing just a new set of 15 random characters. This is obviously not the intended behaviour.
I can't seem to workout what I am doing wrong exactly.
You are using .push() method which adds one or more elements to the end of Array. It will always return new length of the Array.
In your case - .push() is adding new generated array to previously modified array.
let passwordLeft = [];
let passwordRight = [];
let passwordLength = 15;
let characters = `abcdefghijklmnopqrstuvwxyz`;
function generatePassword() {
for (let i = 0; i < passwordLength; i++) {
let leftPW = characters[Math.floor(Math.random() * characters.length)];
let rightPW = characters[Math.floor(Math.random() * characters.length)];
passwordLeft[i] = leftPW;
passwordRight[i] = rightPW;
};
console.log(passwordLeft.join(""));
console.log(passwordRight.join(""));
}
generatePassword();

How to display non-repetitive letter within 3 boxes?

The below code can display a single random letter in each box. However, a letter should not be able to appear on different boxes at the same time as one of the boxes. For example, box 1 displays "A", then box 2 and 3 cannot display "A" also.
function random() {
var letter = [];
for (var i = 65; i < 91; i++)
{
letter.push(String.fromCharCode(i));
}
return letter[Math.floor(Math.random() * letter.Length)];
}
function display()
{
document.getElementById("box1").textContent = random();
document.getElementById("box2").textContent = random();
document.getElementById("box3").textContent = random();
}
A good way to do this would be to overhaul your random function to generate all the letters at once, like so:
function randomN(n=3) {
const letters = new Set()
while (letters.size < 3) {
const i = Math.floor(Math.random() * (91-65)) + 65
letters.add(String.fromCharCode(i))
}
return [...letters]
}
function display() {
const [letter1, letter2, letter3] = randomN()
document.getElementById("box1").textContent = letter1
document.getElementById("box2").textContent = letter2
document.getElementById("box3").textContent = letter3
}
For a more modern approach you can utilize generators:
function* randomLetters() {
const letters = "QWERTYUIOPASDFGHJKLZXCVBNM".split('')
while (letters.length > 0) {
const i = Math.floor(Math.random() * letters.length)
yield letters[i]
letters.splice(i, 1)
}
}
function display() {
const letters = randomLetters()
document.getElementById("box1").textContent = letters.next()
document.getElementById("box2").textContent = letters.next()
document.getElementById("box3").textContent = letters.next()
/* and so on and so forth! */
}
function letterGenerator(n=1) {
var generated_letters = [];
var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
for (var i=0; i<n; i++){
var random = Math.floor(Math.random() * alphabet.length);
var letter = alphabet.splice(random, 1)[0];
generated_letters.push(letter)
}
return generated_letters;
}
var letters = letterGenerator(3)
gives Array(3) [ "Q", "T", "I" ], for example.
by using splice, we are making sure the randomly chosen letters are removed from the alphabet variable.
you can then go over the letters with for (letter of letters) or something like that, and add each one to the desired element.
by the way, maybe run a document.querySelectorAll('[id^="box"]'); to get all elements and add to them with a for loop.
this, alongside the n parameter, allows for any number of generated letters.
(if you really want it to be generic, create the box elements using js as well)
the solutions attached in the comments are certainly clever.
I especially liked this one

Generating non-repeating random numbers in JS

I have the following function
function randomNum(max, used){
newNum = Math.floor(Math.random() * max + 1);
if($.inArray(newNum, used) === -1){
console.log(newNum + " is not in array");
return newNum;
}else{
return randomNum(max,used);
}
}
Basically I am creating a random number between 1 - 10 and checking to see if that number has already been created, by adding it to an array and checking the new created number against it. I call it by adding it to a variable..
UPDATED:
for(var i=0;i < 10;i++){
randNum = randomNum(10, usedNums);
usedNums.push(randNum);
//do something with ranNum
}
This works, but in Chrome I get the following error:
Uncaught RangeError: Maximum call stack size exceeded
Which I guess it's because I am calling the function inside itself too many times. Which means my code is no good.
Can someone help me with the logic? what's a best way to make sure my numbers are not repeating?
If I understand right then you're just looking for a permutation (i.e. the numbers randomised with no repeats) of the numbers 1-10?
Maybe try generating a randomised list of those numbers, once, at the start, and then just working your way through those?
This will calculate a random permutation of the numbers in nums:
var nums = [1,2,3,4,5,6,7,8,9,10],
ranNums = [],
i = nums.length,
j = 0;
while (i--) {
j = Math.floor(Math.random() * (i+1));
ranNums.push(nums[j]);
nums.splice(j,1);
}
So, for example, if you were looking for random numbers between 1 - 20 that were also even, then you could use:
nums = [2,4,6,8,10,12,14,16,18,20];
Then just read through ranNums in order to recall the random numbers.
This runs no risk of it taking increasingly longer to find unused numbers, as you were finding in your approach.
EDIT: After reading this and running a test on jsperf, it seems like a much better way of doing this is a Fisher–Yates Shuffle:
function shuffle(array) {
var i = array.length,
j = 0,
temp;
while (i--) {
j = Math.floor(Math.random() * (i+1));
// swap randomly chosen element with current element
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
var ranNums = shuffle([1,2,3,4,5,6,7,8,9,10]);
Basically, it's more efficient by avoiding the use of 'expensive' array operations.
BONUS EDIT: Another possibility is using generators (assuming you have support):
function* shuffle(array) {
var i = array.length;
while (i--) {
yield array.splice(Math.floor(Math.random() * (i+1)), 1)[0];
}
}
Then to use:
var ranNums = shuffle([1,2,3,4,5,6,7,8,9,10]);
ranNums.next().value; // first random number from array
ranNums.next().value; // second random number from array
ranNums.next().value; // etc.
where ranNums.next().value will eventually evaluate to undefined once you've run through all the elements in the shuffled array.
Overall this won't be as efficient as the Fisher–Yates Shuffle because you're still splice-ing an array. But the difference is that you're now doing that work only when you need it rather than doing it all upfront, so depending upon your use case, this might be better.
//random number without repetition in JavaScript, Just in one line;
//it can be used as _id;
//it not need to store or check;
const myRnId = () => parseInt(Date.now() * Math.random());
console.log(myRnId()); // any random number included timeStamp;
function Myrand(max,min){
arr=[];
for (i = 0; i < max; i++) {
x = Math.floor( Math.random() * max) + min;
if(arr.includes(x) == true){
i=i-1;
}else{
if(x>max==false){
arr.push(x);
}
}
}
return arr;
}
console.log(Myrand(5,1));
Try this:
var numbers = []; // new empty array
var min, max, r, n, p;
min = 1;
max = 50;
r = 5; // how many numbers you want to extract
for (let i = 0; i < r; i++) {
do {
n = Math.floor(Math.random() * (max - min + 1)) + min;
p = numbers.includes(n);
if(!p){
numbers.push(n);
}
}
while(p);
}
console.log(numbers.join(" - "));
let arr = [];
do {
let num = Math.floor(Math.random() * 10 + 1);
arr.push(num);
arr = arr.filter((item, index) => {
return arr.indexOf(item) === index
});
} while (arr.length < 10);
console.log(arr);
HTML
<p id="array_number" style="font-size: 25px; text-align: center;"></p>
JS
var min = 1;
var max = 90;
var stop = 6; //Number of numbers to extract
var numbers = [];
for (let i = 0; i < stop; i++) {
var n = Math.floor(Math.random() * max) + min;
var check = numbers.includes(n);
if(check === false) {
numbers.push(n);
} else {
while(check === true){
n = Math.floor(Math.random() * max) + min;
check = numbers.includes(n);
if(check === false){
numbers.push(n);
}
}
}
}
sort();
//Sort the array in ascending order
function sort() {
numbers.sort(function(a, b){return a-b});
document.getElementById("array_number").innerHTML = numbers.join(" - ");
}
DEMO
The issue is that as you approach saturation you begin to take longer and longer to generate a unique number "randomly". For instance, in the example you provided above the max is 10. Once the used number array contains 8 numbers it can potentially take a long time for the 9th and 10th to be found. This is probably where the maximum call stack error is being generated.
jsFiddle Demo showing iteration count being maxed
By iterating inside of your recursion, you can see that a large amount of execution occurs when the array is completely saturated, but the function is called. In this scenario, the function should exit.
jsFiddle Demo with early break
if( used.length >= max ) return undefined;
And one last way to accomplish both the iteration checks and the infinite recursion would be like this jsFiddle Demo:
function randomNum(max, used, calls){
if( calls == void 0 ) calls = 0;
if( calls++ > 10000 ) return undefined;
if( used.length >= max ) return undefined;
var newNum = Math.floor(Math.random() * max + 1);
if($.inArray(newNum, used) === -1){
return newNum;
}else{
return randomNum(max,used,calls);
}
}
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript Math.random()</h2>
<p>Math.random() returns a random number between 0 (included) and 1 (excluded):</p>
<p id="demo"></p>
<script>
var storeArray = []
function callRamdom(){
var randomNumber = Math.floor(Math.random() * 5);
return randomNumber;
}
function randomStore(){
var localValue = callRamdom()
var status = false;
for(i=0;i<5; i++){
var aa = storeArray[i];
if(aa!=localValue){
console.log(storeArray[i]+"hhhhh"+ localValue);
if(i==4){
status=true;
}
}
else
break;
}
if(status==true){
storeArray.push(localValue);
}
if(storeArray.length!=5){
randomStore();
}
return storeArray;
}
document.getElementById("demo").innerHTML = randomStore();
</script>
</body>
</html>
while(randArr.length < SIZEOFARRAY){
val = Math.floor((Math.random() * RANGEOFVALUES));
if(randArr.indexOf(val) < 0){
randArr.push(val);
}
}
You can change SIZEOFARRAY to the size of the array you wish to use
and also change RANGEOFVALUES to the range of values you wish to randomize
const GenerateRandomNumbers = (max) => {
let orderNumbers = new Set();
for (let i = 1; ;i++){
let random = Math.floor(Math.random() * max + 1) ;
orderNumbers.add(random);
if (orderNumbers.size == max){
break;
}
}
return orderNumbers;}
function randomNumbers(max) {
function range(upTo) {
var result = [];
for(var i = 0; i < upTo; i++) result.push(i);
return result;
}
function shuffle(o){
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
}
var myArr = shuffle(range(max));
return function() {
return myArr.shift();
};
}
Built a little test, try this on jsfiddle:
var randoms = randomNumbers(10),
rand = randoms(),
result = [];
while(rand != null) {
result.push(rand);
rand = randoms();
}
console.log(result);
Shuffle function courtesy of dzone.com.
This is how I achieve it using underscore.js
To get n integers from min to max values. Where n is the size argument.
var randomNonRepeatingIntFromInterval = function(min, max, size) {
var values = [];
while (values.length < size) {
values.push(Math.floor(Math.random() * ( max - min + 1) + min));
values = _.uniq(values);
}
return values;
}
Sorry this is a new answer to an old question, but this can be done more efficiently with a map. What you're after is random selection rather than non-repeating random. Non-repeating random is nonsensical.
Where _a is the collection, and r is not part of the collection, we lambda the random value r:
function aRandom(f){
var r = Math.random();
aRandom._a[r] ? aRandom(f) : f(r,aRandom._a[r] = 1);
}
aRandom._a = {};
//usage:
aRandom(function(r){ console.log(r) });
Redefine aRandom._a when the browser gets sluggish. To avoid eventual sluggishness, one should really use an UUID generation algo with sufficient entropy so that chances of repeat are effectively zero, rather than brute forcing differentiability. I chose the function name aRandom because latin prefix A- means "away from." Since the more it's used, the further away from random the output. The function produces one million unique values in 2100 ms on a Macbook.
Advantage of the above solution is no need to limit the set. As well, multiple callers can use it at the same time and assume their values are different from all other callers. This is handy for such things as noise jittering distributions with insured no overlaps.
However, it can be modified to return integers as well, so as to restrict ram use to the length supplied:
function aRandom(f,c){
var r = Math.floor(Math.random()*c);
aRandom._a[r] ? aRandom(f,c) : f(r,aRandom._a[r] = 1);
}
aRandom._a = {};
//usage:
var len = 10;
var resultset = [];
for(var i =0; i< len; i++){
aRandom(function(r){ resultset.push(r); }, len);
}
console.log(resultset);
randojs.com makes this a simple one-liner:
randoSequence(1, 10)
This will return an array of numbers from 1 through 10 in random order.
You just need to add the following to the head of your html document, and you can do pretty much whatever you want with randomness easily. Random values from arrays, random jquery elements, random properties from objects, and even preventing repetitions as I've shown here.
<script src="https://randojs.com/1.0.0.js"></script>
Just one solution for reference
const fiveNums = () => {
const ranNum = () => Math.floor(Math.random() * (10 + 1));
let current;
let arr = [];
while(arr.length < 5) {
if(arr.indexOf(current = ranNum()) === -1) {
arr.push(current);
}
}
return arr;
};
fiveNums();
In case no permutation is wanted and/or length shall be variable, here is a solution for non repeating randomized lists/arrays without if-statements:
Shuffle function:
Input:
Array or object(list) of arbitrary length
optional: last key to be filtered (Array: index number, List: String of key)
Output:
random Key
to get your random item use myArrayOrList[key]
// no repeat if old_key is provided
function myShuffle(arr_or_list, old_key = false) {
var keys = Array.from(Object.keys(arr_or_list)); //extracts keys
if (old_key != false) {
keys.splice(keys.indexOf(old_key), 1); // removes old_key from keys
};
var randomKey = keys[Math.floor(Math.random() * keys.length)]; // get random key
return randomKey;
}
//test:
const a = [10, 20, 30, 40, 50, 60];
const b = {
"a": 10,
"bla-bla bla": 20,
"d": 30,
"c": 40
};
var oldKeys_a = [];
var oldKeys_b = [];
oldKeys_a[0] = myShuffle(a);
oldKeys_b[0] = myShuffle(b);
var i;
for (i = 1; i < 10; i++) {
oldKeys_a[i] = myShuffle(a, oldKeys_a[i - 1]);
oldKeys_b[i] = myShuffle(b, oldKeys_b[i - 1]);
}
alert('oldKeys_a: ' + oldKeys_a + '; oldKeys_b: ' + oldKeys_b)
//random...
//>>> oldKeys_a: 1,3,0,0,5,0,4,5,2,3; oldKeys_b: d,a,d,bla-bla bla,a,c,d,bla-bla bla,a,d <<<
Non-repeating range random number generation with the recursive patterns.
const getRandom = (max, memo) => {
if (max != memo.length) {
const pos = Math.floor(Math.random() * max);
if (memo.includes(pos)) {
return getRandom(max, memo);
} else {
return pos;
}
}
}
const random = [];
const range = 6;
for (let index = 0; index < range; index++) {
random.push(getRandom(range, random))
}
console.log('random', random) // random (6) [5, 3, 0, 2, 1, 4]
function getRandomNumberNoRepeat(length){
let numberPick = [0,1,2,3,4,5,6,7,8,9]
return numberPick.sort(() => Math.random() -0.5).slice(0, length)}
console.log(getRandomNumberNoRepeat(3));
let display = document.getElementById("container");
let myArray = [];
let randomiser = (min, max, vals) => {
while (myArray.length < vals) {
let randNum = Math.floor(Math.random() * (max - min + 1) + min);
if (!myArray.includes(randNum)) {
myArray.push(randNum);
}
}
return (display.textContent = myArray.join(" - "));
};
randomiser(1, 35, 7);
Here is one where you can specify how many number you need.
I decided to write this code and make a chrome extension for it to use when playing the lotto. haha. Just a learning experience for me and thanks to all who contributed. I read all posts and I'm better of today than I was yesterday in understanding shuffle and random non repeating numbers.
You don't really want a lost of random numbers. Truly random numbers must be able to repeat.
Truly random number are like throwing dice. Any number can come up next.
Shuffled numbers are like drawing playing cards. Each number can come up only once.
What you are really asking for is to shuffle a list of numbers and then use the first so many numbers from the shuffled list.
Think of making a list of numbers in order, and then using the random number generator to randomly select a number from a copy of that list. Each time, put the selected number at the end of your new list and remove it from the copy of the old list, shortening that list. When you are done, the new list will contain the shuffled numbers and the copy of the old list will be empty.
Alternately, you can take the number selected and use it immediately, shortening the copy of the list by removing the used number. Because you have removed the number from the list, it can't come up again.

Categories

Resources