Code review --- Random link with memory -- Javascript - javascript

var links = ["http://www.google.com/", "http://www.cnn.com/", "http://www.bbc.com/", "http://www.nbc.com/"];
var random = Math.round(Math.random() * 4);
var previous = [];
previous.push(random);
return previous
for (var i = 0; i < previous.length; i++) {
while (previous[i] == random) {
random = Math.round(Math.random() * 4);
}
}
window.location = links[random];
I'm trying to make a code that will be used to lead users to a random site from a set of sites. This will be activated by a button in google sites, I just haven't gotten to the html part. Anyways, when I try to run this code in jsfiddle, the output is just a blank screen. Whats wrong? here's my logic
An array of the set sites.
'random' picks a number between 0 and 4, which corresponds to the sites in the array
an empty array
This pushes 'random's output to the empty array
This for loop checks to see if there is any data in the empty array
While loop says "ok, if random chooses a number already in the array 'previous', I will run random again.
Once an unchosen number is outputted, a new window opens to the chosen site.
Sadly, it's not performing this way. Any tips?
Edit: Jsfiddle

I think a slight reworking of the code might do the trick with particular emphasis on removing the loop:
var links = ["http://www.google.com/", "http://www.cnn.com/", "http://www.bbc.com/", "http://www.nbc.com/"];
var previous = [];
function showLink() {
if (previous.length !== links.length) {
var random = Math.round(Math.random() * ((links.length - 1) - 0) + 0 );
if (previous.indexOf(links[random]) > -1) {
showLink();
} else {
console.log(links[random], previous)
previous.push(links[random]);
}
} else {
console.log('No more links');
}
}
And at this point just keep calling showLink until you run out of links.
Demo

var links = ["http://www.google.com/", "http://www.cnn.com/", "http://www.bbc.com/", "http://www.nbc.com/"];
var random = Math.round(Math.random() * 4);
var previous = [];
previous.push(random);
for (var i = 0; i < previous.length; i++) {
while (previous[i] == random) {
random = Math.round(Math.random() * 4);
}
}
window.location = links[random];
You were missing a bracket, and you had a return statement before your for loop. So of course it wasn't working since the code under the return statement was unreachable the way you had it written.

Related

Generating an array with 3 unique numbers

I'm trying to create an array with three unique random numbers between 1 and 14. I've found similar questions on Stackoverflow and used the code to help me create my existing code.
It works well most of the time, but occasionaly it will create an array with two of the same numbers. Here is the offending code:
function noDuplicates (sideRandom) {
sideArray.splice(sideRandom, 1);
let sideRandom2 = Math.floor(Math.random() * 14) + 1;
sideArray.push(sideRandom2);
console.log("I've had to add " + sideRandom2)
}
function sortNumbers(array) {
array.sort(function(a, b) {
return a - b;
});
}
document.getElementById("randomiser").addEventListener("click", function () {
for (let i = 0; sideArray.length <3; i++) {
let sideRandom = Math.floor(Math.random() * 14) + 1;
console.log(sideRandom);
if (sideArray.includes(sideRandom) === false) {
sideArray.push(sideRandom);
} else {
noDuplicates(sideRandom);
};
}
console.log(sideArray);
});
I suspect the issue is that sometimes the noDuplicates function generates the same random number as sideRandom, but I can't see a way around it. can you help?
Use set with while loop to make sure we got required number of unique random numbers
// Get unique random indexes
const random = (num, count) => {
const set = new Set();
while (set.size < count) {
set.add(Math.floor(Math.random() * num) + 1);
}
return [...set];
};
document.getElementById("randomiser").addEventListener("click", function () {
console.log(random(14, 3));
});
<button id="randomiser"> Get 3 random </button>
I take a look at your code: If there is a double you call noDuplicates and try to get a non double number but there you make some mistakes.
Why using Array#splice method? It will return the array without the first element (you don't user this result) and leave the original unchanged. So this line does anything. By the way why you want to delete the first element, youz didn't add the double random-number so there is anything do delete.
Afterwards you build another new randomnumber and push it to your array without checking. By this you get your dublettes.
Better way: If you finf a double set a flag on true and when you next add a number by this you can add your hint and reset the flag to false. So everything is one function.
document.getElementById("randomiser").addEventListener("click", function () {
let sideArray = [];
let double = false;
for (let i= 0; sideArray.length <3; i++) {
let sideRandom = Math.floor(Math.random() * 14) + 1;
console.log(sideRandom);
if (sideArray.includes(sideRandom) === false) {
if (double) {
double = false;
console.log("I've had to add " + sideRandom);
}
sideArray.push(sideRandom);
} else {
double = true;
}
}
console.log(sideArray.toString());
});
<button id='randomiser'>Click</button>
You can do this pretty easily with rando.js and slice. Plus, it's human-readable and cryptographically secure. randoSequence(1, 14) creates a shuffled array of all numbers from 1 through 14, and slice(0, 3) slices out the first three values from that shuffled array.
console.log(randoSequence(1, 14).slice(0, 3));
<script src="https://randojs.com/2.0.0.js"></script>

Random lottery number generator

What I'm trying to do is generate 6 random numbers, five in a range of 1-45 and one in a range of 1-25 for a Greek lottery game (Tzoker). The first 5 numbers should be unique. By pressing a button, I want to add these numbers to a div using jQuery (I have some working code for this part).
I thought it would be pretty easy using a loop, but I've found myself unable to check if the number generated already exists. The loop would only contain the first 5 numbers, because the last number can be equal to one of the other 5.
Let me propose you some simpler solution.
Make a list of all numbers from 1 to 45.
Sort the list using Math.random (plus minus something, read the docs of Array.sort to find out) as the comparison function. You will get the list in random order.
Take 5 first items from the list.
Then, when you already have the numbers, put them all into your div.
This way you don't mix your logic (getting the numbers) with your presentation (putting stuff into the DOM).
I leave the implementation as an exercise for the reader. :)
Like this?
$(function() {
$('button').on('click', function(e) {
e.preventDefault();
var numArray = [];
while( numArray.length < 5 ) {
var number = Math.floor((Math.random() * 45 ) + 1);
if( $.inArray( number, numArray ) == -1 ) {
numArray.push( number );
}
}
numArray.push( Math.floor((Math.random() * 25 ) + 1) );
$('div').html( numArray.join("<br />") );
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Generate</button>
<div></div>
While this might be not exactly what you were asking for, if you would use lodash, this would be as simple as:
_.sample(_.range(1, 46), 5) // the 5 numbers from 1..45
_.random(1, 26) // one more from 1..25
This is why functional programming is so cool. You can read for example Javascript Allonge to find out more.
http://jsfiddle.net/015d05uu/
var tzoker = $("#tzoker");
var results = $("#results");
tzoker.click(function() {
results.empty();
var properResults = [];
var rand = 0;
var contains = false;
for (i = 1; i < 7; i++) {
do
{
(rand = Math.floor((Math.random() * (i != 6 ? 45 : 25)) + 1));
contains = properResults.indexOf(rand) > -1;
} while(contains)
results.append("<br />", rand, "<br />");
properResults.push(rand);
}
});
Here is the main idea of a solution. You can define the max value as a parameter for the random.
Then, check the existence of the item in a simple array with only the data you want.
You may use a general function which generates random numbers from 1 to maxValue, and adds them to an array only if they don't exist. Then, to display, cycle through the array items and append them to #randomNumbers.
HTML
<div id="randomNumbers"></div>
JS (with jQuery)
var randomNumbersArray = [];
$(function() {
generateRandomNumbers();
displayRandomNumbers();
});
function generateRandomNumbers() {
for (i = 0; i < 5; i++) {
generateRandomNumberFrom1To(45);
}
generateRandomNumberFrom1To(25);
}
function generateRandomNumberFrom1To(maxValue) {
var randomNumber;
do {
randomNumber = Math.ceil(Math.random() * maxValue);
} while ($.inArray(randomNumber, randomNumbersArray) > -1);
randomNumbersArray.push(randomNumber);
}
function displayRandomNumbers() {
for (i in randomNumbersArray) {
$("#randomNumbers").append(randomNumbersArray[i] + "<br>");
}
}

Uncontrolled iteration in loop

Thats my code:
var randomCoord = function(cells) {
var step = $('.workplace').innerWidth()/cells;
var xCord = (Math.floor(Math.random() * (cells+1)))*step;
var yCord = (Math.floor(Math.random() * (cells+1)))*step;
if(plants.length != 0) {
for (var i=0; i<plants.length; i++) {
if (plants[i].left != xCord && plants[i].top != yCord) {
plants.push({"top": yCord, "left": xCord});
}
}
} else {
plants.push({"top": yCord, "left": xCord});
}
};
var multiplayer = function(func, endIteration, cells) {
for (var i=0; i<endIteration; i++) {
func(cells);
};
};
multiplayer(randomCoord, 5, 10) // will iterate diferent times
Function, multiplayer have to run "randomCoords" 5 times, but it's not working. Why quantity of iteration is uncontroled? How can I fix it?
It looks like your for loop in randomCoord() is supposed to be only pushing an entry into the array if the coordinates don't already exist in the array, but that isn't how your logic works. Instead, you check each and every item in the array and if it's not equal to that item in the array, you push it and you do that for each item in the array so you end up with lots of duplicates in the array (exactly what you're trying to prevent).
So, the first time you call randomCoord, you get one item. The next time you call it, you get two items. The third time you call it you get 4 items, then 8, then 16. This is a fairly simple logic error.
If you just want to add one unique item each time you call randomCoord, then you could use logic like this:
var randomCoord = function(cells) {
var step = $('.workplace').innerWidth()/cells;
var xCord = (Math.floor(Math.random() * (cells+1)))*step;
var yCord = (Math.floor(Math.random() * (cells+1)))*step;
var found = false;
for (var i=0; i<plants.length; i++) {
if (plants[i].left == xCord && plants[i].top == yCord) {
found = true;
break;
}
}
if (!found) {
plants.push({"top": yCord, "left": xCord});
}
};
Note, you don't need the separate if (plants.length != 0) because the for loop already checks that and our new found variable handles the case where the array is initially empty.
If you happen to generate a coordinate conflict, this will add no item on that function call though the odds of generating two conflicting random values are fairly low as long as cells*step is a decent size number (the range of your random number generator). If you want to try it again in that case, then you need another loop to try again if a conflict is found.

Javascript challenge - which basket contains the last apple?

I'm presented with the following challenge question:
There are a circle of 100 baskets in a room; the baskets are numbered
in sequence from 1 to 100 and each basket contains one apple.
Eventually, the apple in basket 1 will be removed but the apple in
basket 2 will be skipped. Then the apple in basket 3 will be removed.
This will continue (moving around the circle, removing an apple from a
basket, skipping the next) until only one apple in a basket remains.
Write some code to determine in which basket the remaining apple is
in.
I concluded that basket 100 will contain the last apple and here's my code:
var allApples = [];
var apples = [];
var j = 0;
var max = 100;
var o ='';
while (j < max) {
o += ++j;
allApples.push(j);
}
var apples = allApples.filter(function(val) {
return 0 == val % 2;
});
while (apples.length > 1) {
for (i = 0; i < apples.length; i += 2) {
apples.splice(i, 1);
}
}
console.log(apples);
My question is: did I do this correctly? What concerns me is the description of "a circle" of baskets. I'm not sure this is relevant at all to how I code my solution. And would the basket in which the remaining apple reside be one that would otherwise be skipped?
I hope someone can let me know if I answered this correctly, answered it partially correct or my answer is entirely wrong. Thanks for the help.
So, ... I got WAY too into this question :)
I broke out the input/output of my last answer and that revealed a pretty simple pattern.
Basically, if the total number of items is a power of 2, then it will be the last item. An additional item after that will make the second item the last item. Each additional item after that will increase the last item by 2, until you reach another item count that is again divisible by a power of 2. Rinse and repeat.
Still not a one-liner, but will be much faster than my previous answer. This will not work for 1 item.
var items = 100;
function greatestPowDivisor(n, p) {
var i = 1;
while(n - Math.pow(p, i) > 0) {
i++;
}
return Math.pow(p, (i - 1));
}
var d = greatestPowDivisor(items, 2)
var last_item = (items - d) * 2;
I believe Colin DeClue is right that there is a single statement that will solve this pattern. I would be really interested to know that answer.
Here is my brute force solution. Instead of moving items ("apples") from their original container ("basket") into a discard pile, I am simply changing the container values from true or false to indicate that an item is no longer present.
var items = 100;
var containers = [];
// Just building the array of containers
for(i=0; i<items; i++) {
containers.push(true);
}
// count all containers with value of true
function countItemsLeft(containers) {
total = 0;
for(i=0; i<containers.length; i++) {
if(containers[i]) {
total++;
}
}
return total;
}
// what is the index of the first container
// with a value of true - hopefully there's only one
function getLastItem(containers) {
for(i=0; i<containers.length; i++) {
if(containers[i]) {
return(i);
}
}
// shouldn't get here if the while loop did it's job
return false;
}
var skip = false;
// loop through the items,
// setting every other to false,
// until there is only 1 left
while(countItemsLeft(containers) > 1) {
for(i=0; i<containers.length; i++) {
if(containers[i]) {
if(skip) {
skip = false;
} else {
containers[i] = false;
skip = true;
}
}
}
}
// what's the last item? add one to account for 0 index
// to get a human readable answer
var last_item = getLastItem(containers) + 1;
Needs error checking, etc... but it should get the job done assuming items is an integer.

Generate unique number within range (0 - X), keeping a history to prevent duplicates

I ran into the challenge where I need a function that returns a random number within a given range from 0 - X. Not only that, but I require the number returned to be unique; not duplicating numbers that have already been returned on previous calls to the function.
Optionally, when this is done (e.g. the range has been 'exhausted'), just return a random number within the range.
How would one go about doing this?
This should do it:
function makeRandomRange(x) {
var used = new Array(x),
exhausted = false;
return function getRandom() {
var random = Math.floor(Math.random() * x);
if (exhausted) {
return random;
} else {
for (var i=0; i<x; i++) {
random = (random + 1) % x;
if (random in used)
continue;
used[random] = true;
return random;
}
// no free place found
exhausted = true;
used = null; // free memory
return random;
}
};
}
Usage:
var generate = makeRandomRange(20);
var x1 = generate(),
x2 = generate(),
...
Although it works, it has no good performance when the x-th random is generated - it searches the whole list for a free place. This algorithm, a step-by-step Fisher–Yates shuffle, from the question Unique (non-repeating) random numbers in O(1)?, will perform better:
function makeRandomRange(x) {
var range = new Array(x),
pointer = x;
return function getRandom() {
pointer = (pointer-1+x) % x;
var random = Math.floor(Math.random() * pointer);
var num = (random in range) ? range[random] : random;
range[random] = (pointer in range) ? range[pointer] : pointer;
return range[pointer] = num;
};
}
(Demo at jsfiddle.net)
Extended version which does only generate one "group" of unique numbers:
function makeRandomRange(x) {
var range = new Array(x),
pointer = x;
return function getRandom() {
if (range) {
pointer--;
var random = Math.floor(Math.random() * pointer);
var num = (random in range) ? range[random] : random;
range[random] = (pointer in range) ? range[pointer] : pointer;
range[pointer] = num;
if (pointer <= 0) { // first x numbers had been unique
range = null; // free memory;
}
return num;
} else {
return Math.floor(Math.random() * x);
}
};
}
(Demo)
You got some great programming answer. Here's one with a more theoretical flavor to complete your panorama :-)
Your problem is called "sampling" or "subset sampling" and there are several ways you could do this. Let N be the range you are sampling frame (i.e., N=X+1) and M be the size of your sample (the number of elements you want to pick).
if N is much larger than M, you'll want to use an algorithm such as the one suggested by Bentley and Floyd in his column "Programming Pearls: a sample of brilliance" (temporarily available without ACM's lock screen here), I really recommend this as they explicitly give code and discuss in terms of hash tables, etc.; there a few neat tricks in there
if N is within the same range as M, then you might want to use the Fisher-Yates shuffle but stop after only M steps (instead of N)
if you don't really know then the algorithm on page 647 of Devroye's book on random generation is pretty fast.
I wrote this function. It keeps its own array with a history of generated numbers, preventing initial duplicates, continuing to output a random number if all numbers in the range have been outputted once:
// Generates a unique number from a range
// keeps track of generated numbers in a history array
// if all numbers in the range have been returned once, keep outputting random numbers within the range
var UniqueRandom = { NumHistory: new Array(), generate: function(maxNum) {
var current = Math.round(Math.random()*(maxNum-1));
if (maxNum > 1 && this.NumHistory.length > 0) {
if (this.NumHistory.length != maxNum) {
while($.inArray(current, this.NumHistory) != -1) { current = Math.round(Math.random()*(maxNum-1)); }
this.NumHistory.push(current);
return current;
} else {
//unique numbers done, continue outputting random numbers, or we could reset the history array (NumHistory = [];)
return current;
}
} else {
//first time only
this.NumHistory.push(current);
return current;
}
}
};
Here's a working Fiddle
I hope this is of use to someone!
Edit: as pointed out by Pointy below, it might get slow with a large range (here is a
fiddle, going over a range from 0-1000, which seems to run fine). However; I didn't require a very large range, so perhaps this function is indeed not suited if you look to generate and keep track of an enormous range.
You may try generating the number using the current date and time value which would make it unique. To make it within the range, you may have to use some mathematical function.

Categories

Resources