Related
I've been having an issue with some code in a project I'm doing at school. I have an array of length 4, where each element of the array is an integer. The values of the integers are determined through multiplying Math.random() by the length of another array. The goal of my code is to find some way of checking whether any of the elements are equal to each other, and if they are then I would want to "reset" (redefine) them until they aren't equal. Here's what I just described, albeit in a very basic form:
var originalArray=[a, b, c, d, e, f, g];
var or1=Math.floor(Math.random() * originalArray.length);
var or2=Math.floor(Math.random() * originalArray.length);
var or3=Math.floor(Math.random() * originalArray.length);
var or4=Math.floor(Math.random() * originalArray.length);
var numArray=[or1, or2, or3, or4];
function resetData(){
or1=Math.floor(Math.random() * originalArray.length);
or2=Math.floor(Math.random() * originalArray.length);
or3=Math.floor(Math.random() * originalArray.length);
or4=Math.floor(Math.random() * originalArray.length);
numArray=[or1, or2, or3, or4];
}
I then have another function, at the beginning of which I want to run resetData. However, if any of the elements are equal to each other then I want it to run again until none of them are.
function startOperations(){
resetData();
//insert other code here
}
I'm stuck on what I should do to make it only reset under the previously-mentioned conditions. My initial idea was to do a for-loop, kind of like this:
for(i=0; i<songNumArray.length-1; i++){
if(songNumArray[i]==songNumArray[i+1]){
resetData();
}
}
But I now realize that this won't work, since this only checks if the elements next to each other are equal. So I tried just an if-statement:
if(songNumArray[0]==songNumArray[1]||songNumArray[0]==songNumArray[2]||songNumArray[0]==songNumArray[3]){
resetData();
}
This doesn't work either, of course, which is why I'm here. Does anyone on here have an idea of what I could do?
Rather than declaring variables for each element of the array first and putting them into an array later, I'd suggest simply using a array to start with - that way you can use the array methods. You also don't need to reset all of the elements in the array and retry until something works - it would be more elegant to try to generate new elements and insert them only if they pass. For example:
function createArrayWithoutDuplicates(highestValMinusOne, length) {
const highestVal = highestValMinusOne + 1;
const arr = [];
while (arr.length < length) {
let val;
do {
val = Math.floor(Math.random() * highestVal);
} while (arr.includes(val));
arr.push(val);
}
return arr;
}
console.log(createArrayWithoutDuplicates(10, 7));
console.log(createArrayWithoutDuplicates(10, 7));
console.log(createArrayWithoutDuplicates(10, 7));
console.log(createArrayWithoutDuplicates(10, 7));
console.log(createArrayWithoutDuplicates(10, 7));
console.log(createArrayWithoutDuplicates(10, 7));
You can do it with 2 for loops where first one is from first element to second last element and the second loop always starts with the next element that of the first loop and ends with last element of the array.
for(i = 0; i < songNumArray.length - 1; i++){
for(j = i + 1; j < songNumArray.length; j++) {
if(songNumArray[i] == songNumArray[j]){
resetData();
}
}
}
You could use a Set like this to look for duplicates:
let dupes;
do {
let set = new Set();
dupes = false;
for (let i = 0; i < songNumArray.length; ++i) {
if (set.has(songNumArray[i]) {
resetData();
dupes = true;
break;
}
else {
set.add(songNumArray[i]);
}
}
} while (dupes);
I've added a getUniqueNumber function that generates a unique number if it's a duplicate.
Commented code below;
var originalArray = [1, 2, 3, 4, 5, 6, 7];
var or1 = Math.floor(Math.random() * originalArray.length);
var or2 = Math.floor(Math.random() * originalArray.length);
var or3 = Math.floor(Math.random() * originalArray.length);
var or4 = Math.floor(Math.random() * originalArray.length);
var numArray = [or1, or2, or3, or4];
function getUniqueNumber(index) {
var val = numArray[index];
// check if subsequent values are equal to current value.
// if not return current value.
// no need to check previous values as they are already unique.
if (numArray.indexOf(val, index + 1) === -1)
return val;
// repeat until unique number is generated
while (true) {
val = Math.floor(Math.random() * originalArray.length);
if (numArray.indexOf(val) === -1)
return val;
}
}
function resetData() {
for (let i = 0; i < 4; i++) {
numArray[i] = getUniqueNumber(i);
}
}
function startOperations() {
console.log("before: " + numArray);
resetData();
console.log("after: " + numArray);
//insert other code here
}
startOperations();
Just use another for loop to iterate again. Something like this:
for(i=0; i<songNumArray.length; i++){
for(j=i+1; j<songNumArray.length; j++){
if(songNumArray[i]==songNumArray[j]){
resetData();
}
}
}
I would use nested for-loops. That way, we can cross reference all elements of the array with each other.
function newData() {
for (var i = 0; i < originalArray.length; i++) {
for (var j = 0; j < originalArray.length; j++) {
if (i != j && numArray[i] == numArray[j]) {
console.log('find');
numArray[i] = Math.floor(Math.random() * originalArray.length);
i = 0;
}
}
}
}
If you look at the if-statement, you can see I first check to make sure it doesn't equal j, since a number will always equal itself, and we don't want to cross reference the same elements in the array. Next, I check if they're the same. If they are, I set i to 0. This acts similar to recursion, it "resets" or "re-runs" the loop. If a number gets set to an already existing number, it will go through the if statement again, modifying it until it meets the criteria. Hope this helps!
I have a problem. When I shuffle a array starting from position 1 and ending on position 49, one element seems to be undefined when outputted in the console.
for (var i = 49; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = genNr[i];
genNr[i] = genNr[j];
genNr[j] = temp;
}
Please help me.
I'm thinking that genNr contains 49 elements. The first time the loop runs, i will be 49 so you're trying to access genNr[49]. But, since array indexes start at zero, genNr[49] is undefined.
You need to start the loop with i equal to the length of the array minus 1, and stop when i is less than zero.
for (var i = genNr.length - 1; i >= 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = genNr[i];
genNr[i] = genNr[j];
genNr[j] = temp;
}
I need to generate 30 Random Numbers between 1 to 20. I am using this code but this is making conflict since the loop number is bigger than the Random range (I guess!)
var arr1 = [];
for (var i = 0; i < 30;) {
var ran = Math.floor(Math.random() * 20) + 1;
if ( arr1.indexOf(ran) == -1)
arr1[i++] = ran;
}
can you please let me know why this is happening and how I can prevent this to create 30 random numbers?
Thanks
I created a fiddle here , have a look
https://jsbin.com/kagixi/edit?html,js,output
In first case, we are updating values by iterating over all the indices.
var list = new Array(30).fill(0);
list.forEach(function(d, index){
list[index] = Math.floor(Math.random() * 20) + 1;
});
console.log(list);
Another way of doing this is to initialize array just to loop and then simply create a new array with same length or replace the existing one as I did in this example.
var list2 = new Array(30).fill(0);
list2 = list2.map(function(){
return Math.floor(Math.random() * 20) + 1;
});
You miss the third argument on your for statement.
Try this:
var arr1 = [];
for (var i = 0; i < 30; i++) {
arr1[i] = Math.floor(Math.random() * 20) + 1;
}
Your code creates an infinite loop.
Your random numbers are between 1 and 20 but you want 30 of them in your array. The check for duplicates in the line if ( arr1.indexOf(ran) == -1) is guaranteed to return false after 20 iterations, creating an infinite loop.
// Contains a list of items in each set.
var sets[0] = [1,2,3,4,5,6,7,8,9],
sets[1] = [10,11,12,13,14,15,16,17,18],
sets[2] = [19,20,21,22,23,25,26,27]
// Contains the mins associated to each set item.
var setTimes[0] = [15,15,15,15,15,15,15,15,15],
setTimes[1] = [16,12,11,15,13,15,15,15,14],
setTimes[2] = [16,12,11,15,13,12,11,15,13]
I've got a set of arrays as given above. The sets array has a data set of values. This array can have n number of items in it. Ex, sets[n].
Each sets array has an equivalent setTimes array that has minutes stored in it. setTimes[0][0] is 15min and is the number of minutes for sets[0][0].
Given a set item(ex 12), I'd like to:
Find out which set array does the given number belong to? In our case, since 12 was the item, it belongs to sets[1].
Once I have this, I'd like to get the sum of all mins from the setTimes array for the current sets index and also the next index. In our case, that would be the sum of setTimes[1] and setTimes[2].
In the event we reach the end of sets array, I'd like to get the sum of the first set array.
For ex,
- if I pass 12, I'll need to get the sum of setTimes[1] and setTimes[2]
- If I pass 23, I'll need to get the sum of setTimes[2] and setTimes[0]
Here is the loop I've been thinking, would like to know if there is a better way of doing this.
function computeTotalMin(givenItem)
{
// represents how many sets to loop thorough. I need 2.
for (x = 0; x <= 1; x++)
{
for(i = 0; i < sets.length; i++)
{
// checking to see if i value is on the last index of the sets array.
if(i === sets.length - 1)
{
i = 0;
var item = sets[i].indexOf(givenItem);
if(item !== -1)
{
// Loops through all mins from setTimes[i] array
for(j = 0; j < setTimes[i].length; j++)
{
var total = total + setTimes[j];
}
}
}
}
}
}
You don't need two nested loops for continuing at the end. You should have a single loop that iterates the number of sets you're interested in (2), and has an index (starting at the one set you've found). Inside that loop, you'd make a modulo operation on the index; to get back to the start when you've reached the end. By only looping over the count, not the (resettable) index, you won't get into an infinite loop.
You also should divide your program in just those tasks that you've textually described (find this, then do that), instead of munching everything in one huge nested control structure.
function computeTotalMin(givenItem) {
var setIndex = -1;
for (; setIndex < sets.length; setIndex++)
if (sets[setIndex].indexOf(givenItem) > -1)
break;
if (setIndex == sets.length)
return null; // givenItem found in none of the sets
var sum = 0;
for (var count = 0; count < 2; count++) {
for (var i=0; i<setTimes[setIndex].length; i++)
sum += setTimes[setIndex][i];
setIndex++; // go to next set
setIndex %= sets.length; // which might be 0
// alternatively: if (setIndex == sets.length) setIndex = 0;
}
return sum;
}
Ok so i need to create four randomly generated numbers between 1-10 and they cannot be the same. so my thought is to add each number to an array but how can I check to see if the number is in the array, and if it is, re-generate the number and if it isnt add the new number to the array?
so basically it will go,
1.create new number and add to array
2.create second new number, check to see if it exist already, if it doesn't exist, add to array. If it does exist, re-create new number, check again etc...
3.same as above and so on.
You want what is called a 'random grab bag'. Consider you have a 'bag' of numbers, each number is only represented once in this bag. You take the numbers out, at random, for as many as you need.
The problem with some of the other solutions presented here is that they randomly generate the number, and check to see if it was already used. This will take longer and longer to complete (theoretically up to an infinite amount of time) because you are waiting for the random() function to return a value you don't already have (and it doesn't have to do that, it could give you 1-9 forever, but never return 10).
There are a lot of ways to implement a grab-bag type solution, each with varying degrees of cost (though, if done correctly, won't ever be infinite).
The most basic solution to your problem would be the following:
var grabBag = [1,2,3,4,5,6,7,8,9,10];
// randomize order of elements with a sort function that randomly returns -1/0/1
grabBag.sort(function(xx,yy){ return Math.floor(Math.random() * 3) - 1; })
function getNextRandom(){
return grabBag.shift();
};
var originalLength = grabBag.length;
for(var i = 0; i < originalLength; i++){
console.log(getNextRandom());
}
This is of course destructive to the original grabBag array. And I'm not sure how 'truly random' that sort is, but for many applications it could be 'good enough'.
An slightly different approach would be to store all the unused elements in an array, randomly select an index, and then remove the element at that index. The cost here is how frequently you are creating/destroying arrays each time you remove an element.
Here are a couple versions using Matt's grabBag technique:
function getRandoms(numPicks) {
var nums = [1,2,3,4,5,6,7,8,9,10];
var selections = [];
// randomly pick one from the array
for (var i = 0; i < numPicks; i++) {
var index = Math.floor(Math.random() * nums.length);
selections.push(nums[index]);
nums.splice(index, 1);
}
return(selections);
}
You can see it work here: http://jsfiddle.net/jfriend00/b3MF3/.
And, here's a version that lets you pass in the range you want to cover:
function getRandoms(numPicks, low, high) {
var len = high - low + 1;
var nums = new Array(len);
var selections = [], i;
// initialize the array
for (i = 0; i < len; i++) {
nums[i] = i + low;
}
// randomly pick one from the array
for (var i = 0; i < numPicks; i++) {
var index = Math.floor(Math.random() * nums.length);
selections.push(nums[index]);
nums.splice(index, 1);
}
return(selections);
}
And a fiddle for that one: http://jsfiddle.net/jfriend00/UXnGB/
Use an array to see if the number has already been generated.
var randomArr = [], trackingArr = [],
targetCount = 4, currentCount = 0,
min = 1, max = 10,
rnd;
while (currentCount < targetCount) {
rnd = Math.floor(Math.random() * (max - min + 1)) + min;
if (!trackingArr[rnd]) {
trackingArr[rnd] = rnd;
randomArr[currentCount] = rnd;
currentCount += 1;
}
}
alert(randomArr); // Will contain four unique, random numbers between 1 and 10.
Working example: http://jsfiddle.net/FishBasketGordo/J4Ly7/
var a = [];
for (var i = 0; i < 5; i++) {
var r = Math.floor(Math.random()*10) + 1;
if(!(r in a))
a.push(r);
else
i--;
}
That'll do it for you. But be careful. If you make the number of random numbers generated greater than the may number (10) you'll hit an infinite loop.
I'm using a recursive function. The test function pick 6 unique value between 1 and 9.
//test(1, 9, 6);
function test(min, max, nbValue){
var result = recursValue(min, max, nbValue, []);
alert(result);
}
function recursValue(min, max, nbValue, result){
var randomNum = Math.random() * (max-min);
randomNum = Math.round(randomNum) + min;
if(!in_array(randomNum, result)){
result.push(randomNum);
nbValue--;
}
if(nbValue>0){
recursValue(min, max, nbValue, result);
}
return result;
}
function in_array(value, my_array){
for(var i=0;i< my_array.length; i++){
if(my_array[i] == value){
console.log(my_array+" val "+value);
return true;
}
}
return false;
}
Here is a recursive function what are you looking for.
"howMany" parameter is count of how many unique numbers you want to generate.
"randomize" parameter is biggest number that function can generate.
for example : rand(4,8) function returns an array that has 4 number in it, and the numbers are between 0 and 7 ( because as you know, Math.random() function generates numbers starting from zero to [given number - 1])
var array = [];
var isMatch= false;
function rand(howMany, randomize){
if( array.length < howMany){
var r = Math.floor( Math.random() * randomize );
for( var i = 0; i < howMany; i++ ){
if( array[i] !== r ){
isMatch= false;
continue;
} else {
isMatch= true;
break;
}
}
if( isMatch == false ){
array.push(r);
ran(howMany, randomize);
}
ran(howMany, randomize);
return array;
}
}
In your answer earlier, you do have a small bug. Instead of
var originalLength = grabBag.length;
for(var i = 0; i < originalLength .length; i++){
console.log(getNextRandom());
}
I believe you meant:
var originalLength = grabBag.length;
for(var i = 0; i < originalLength; i++){
console.log(getNextRandom());
}
Thanks.