The Idea is: click a button to replace a headline with a unique string of an array. The problem with this is that I've used a string of the array before like this:
headlines = new Array("Good", "Bad", "Ugly", "Random Headline");
var randomNumberBefore = 4;
alert (headlines[randomNumberBefore]);
but I dont want to display the same headline again, thatswhy it is key check that the actual index randomNumberBefore is not the same number like new index randomNumber. The following function makes sense to me, but sometimes it returns the same number, that causes the headline is replaced by itself and the user noticed no change.
function randomNumberByRange (range, number) {
var r;
do {
var r = Math.floor(Math.random() * range);
} while (r == number);
return r;
}
$(document).on('click','.nextquote' , function() {
var randomNumber = randomNumberByRange( headlines.length, randomNumberBefore);
var nextHeadline = headlines[randomNumber];
$(".bannertext").text(nextHeadline);
console.log(nextHeadline);
});
Any Ideas to get unique headlines per click?
Here is my starting fiddle.
--
Here is the final fiddle.
You forgot to assign the old value to randomNumberBefore;
after
var randomNumber = randomNumberByRange( headlines.length, randomNumberBefore);
put
randomNumberBefore = randomNumber;
PS: There is a way to make the randomNumberByRange function more performant:
function randomNumberByRange (range, number) {
var r = Math.floor(Math.random() * (range-1));
if(r >= number)r++;
return r;
}
but, if you have many headlines, your function is good enough (as collision probability drops with the number of items you have in the list)
If you don't want repetition, simply remove the used elements from the array.
var h = new Array("Good", "Bad", "Ugly", "Random Headline");
while (h.length > 0) {
var i = Math.floor(Math.random() * h.length);
var t = h.splice(i, 1);
console.log(t);
}
Related
Trying to understand an answer from a previous question.
I found this question and subsequently the answer provided by #obscure, which I was able to modify based on what I needed. It worked perfectly! So I'm just trying to make sure I learn and understand exactly what's happening here.
Best I understand currently:
tempA = fileNames[a]; and tempA = trackTitles[a]; are temporarily storing the current location in the iteration, but I'm not sure what happens at fileNames[tempB] = tempA; and trackTitles[tempB] = tempA;.
tempB = Math.floor(Math.random() * fileNames.length); is generating a random index within the questions array.
fileNames[a] = fileNames[tempB]; and trackTitles[a] = trackTitles[tempB]; are swapping the current index with the randomly generated index.
Any help is appreciated!
this.fileNames = ["fileA", "fileB", "fileC"];
this.trackTitles = ["titleA", "titleB", "titleC"];
function shuffle() {
var tempA;
var tempB;
for (var a = 0; a < fileNames.length; a++) {
tempA = fileNames[a];
tempB = Math.floor(Math.random() * fileNames.length);
fileNames[a] = fileNames[tempB];
fileNames[tempB] = tempA;
tempA = trackTitles[a];
trackTitles[a] = trackTitles[tempB];
trackTitles[tempB] = tempA;
}
}
shuffle();
console.log(fileNames);
console.log(trackTitles);
The code iterates over the array and for each element it stores the element with index a in the variable tempA and a random index is generated and stored in tempB hence the line tempB = Math.floor(Math.random() * fileNames.length);. In the following lines the element at the current Index a and the element at the randomly generated index tempB are swapped, same with thing with the second array. The randomly generated index is used on both arrays so it doesn't missmatch, which is exactly what you want.
This algorithm has a problem tho, because it could potentially reverse itself:
Lets say you have the following array ["a", "b", "c", "d"] and use the above algorithm on that array, then you could potentially swap a → b, b → a, c → d and d → c, ending up with the same array as you began with.
I would recommend you to use the Fisher-Yates Shuffle algorithm. This algorithm also has the chance to return the exacted same array, but it at least doesn't reverse the steps it did before. The idea of the algorithm is that the array builds itself up from a pool of elements you can randomly choose from, and if that element has been chosen, it can't be chosen again and is removed from that pool (It actually works like selection-sort, if you are familiar with that, but instead of choosing the max or min element of the rest, you just choose a random one).
Implementation:
this.fileNames = ["fileA", "fileB", "fileC"];
this.trackTitles = ["titleA", "titleB", "titleC"];
function shuffle() {
let currentIndex = this.fileNames.length-1, randomIndex;
while (currentIndex > 0) {
randomIndex = Math.floor(Math.random() * (currentIndex+1));
[this.fileNames[currentIndex], this.fileNames[randomIndex]] =
[this.fileNames[randomIndex], this.fileNames[currentIndex]];
[this.trackTitles[currentIndex], this.trackTitles[randomIndex]] =
[this.trackTitles[randomIndex], this.trackTitles[currentIndex]];
currentIndex--;
}
}
shuffle();
console.log(fileNames);
console.log(trackTitles);
You could modify the algorithm though so that indices don't choose themselves. This way, you won't get the same exact array.
this.fileNames = ["fileA", "fileB", "fileC", "fileD", "fileE", "fileF"];
this.trackTitles = ["titleA", "titleB", "titleC", "titleD", "titleE", "titleF"];
const generateRandom = (n, ex) => {
const r = Math.floor(Math.random() * (n+1))
return r == ex ? generateRandom(n, ex) : r;
}
function shuffle() {
let currentIndex = this.fileNames.length-1, randomIndex;
while (currentIndex > 0) {
randomIndex = generateRandom(currentIndex, currentIndex);
[this.fileNames[currentIndex], this.fileNames[randomIndex]] =
[this.fileNames[randomIndex], this.fileNames[currentIndex]];
[this.trackTitles[currentIndex], this.trackTitles[randomIndex]] =
[this.trackTitles[randomIndex], this.trackTitles[currentIndex]];
currentIndex--;
}
}
shuffle();
console.log(fileNames);
console.log(trackTitles);
var randomDescriptor1 = descriptors[getRandomIndex(descriptors)];
tagline1.innerText = randomDescriptor1;
var randomDescriptor2 = descriptors[getRandomIndex(descriptors)];
tagline2.innerText = randomDescriptor2;
function getRandomIndex(array) {
return Math.floor(Math.random() * array.length);
}
I am altering an HTML file using dom and want to generate random descriptors in the same sentence. I pull from an array of stored strings, but sometimes they happen to return the same word twice in the sentence. How could I avoid this?
const desc = Object.assign([],descriptors)
var randomDescriptor1 = getRandomDescriptor(desc);
tagline1.innerText = randomDescriptor1;
var randomDescriptor2 = getRandomDescriptor(desc)
tagline2.innerText = randomDescriptor2;
// you can delete have used item, you will always get a new one
function getRandomDescriptor(array) {
const randomNum = Math.floor(Math.random() * array.length);
const value = array[randomNum]
array.splice(randomNum, 1)
return value
}
What is the best way in JavaScript given an array of ids:
var ids = ['ax6484', 'hx1789', 'qp0532'];
and a current id hx1789 to select another value at random that is not the current from the ids array?
Get the index of the value, generate a random value, if the random is the index, use 1 less (depending on random generated)
var random = Math.floor(Math.random() * ids.length)
var valueIndex = ids.indexOf("hx1789");
if (random === valueIndex) {
if (random === 0) {
random = 1;
} else {
random = random - 1;
}
}
var randomValue = ids[random];
Demo: http://jsfiddle.net/sxzayno7/
And yeah, test the array length, if it's 1 - probably don't want to do this! Thanks #ThomasStringer
if (ids.length > 1) { //do it! }
Or just filter out the value and pull against that:
var randomIndex = Math.floor(Math.random() * (ids.length - 1))
var random = ids.filter(function(id) {
return id !== "hx1789";
})[randomIndex]
I would probably do something along the lines of:
var current = 'hx1789'; // whatever variable stores your current element
var random = Math.floor(Math.random() * ids.length);
if(random === ids.indexOf(current)) {
random = (random + 1) % ids.length;
}
current = ids[random];
Basically if the newly picked element sits on the same index, you just pick the next element from the array, or if that goes out of bounds, pick the first.
UnderscoreJS is your best friend!
_.sample(_.without(['ax6484', 'hx1789', 'qp0532'], 'hx1789'));
or with variables;
var myArray = ['ax6484', 'hx1789', 'qp0532'];
var currentId = 'hx1789';
var newRandomId = _.sample(_.without(myArray , currentId));
You could make a duplicate array, then remove the current indexed element from that array and select an random element from the duplicate with the removed item:
var ids = ['ax6484', 'hx1789', 'qp0532'];
var currentIndex = ids.indexOf('hx1789');
var subA = ids.slice(0);
subA.splice(currentIndex , 1);
var randItem = subA[Math.floor((Math.random() * subA.length))];
Example Here.
This gets trickier if your value is not a simple string such as hx1789 but rather for instance comes from an array within the array you want to generate a different value from:
let weekSegments = [["Su","Mo"],["Tu","We"],["Th","Fr","Sa"]];
If you have the index it's simple, no matter how deeply nested the array (or object) is and/or the types of the values it contains:
let index = 1;
let segment = weekSegments[index];
let randomIndex;
let randomSegment;
if (weekSegments.length >= 1) {
randomIndex = Math.floor(Math.random) * (weekSegments.length - 1);
randomSegment = weekSegments.filter((val, i) => i !== index)[randomIndex];
}
Without the index though things get more complicated due to having to test for array and/or object equality in the filter function. In the case above it's fairly easy because it's an array that is only one-level deep which in turn only contains simple values. First we create a string representation of the sorted segment values we already have, then compare that to a string representation of val on each filter iteration:
let segmentVals = segment.sort().join(',');
if (weekSegments.length > 1) {
randomIndex = Math.floor(Math.random) * (weekSegments.length - 1);
randomSegment = weekSegments.filter((val) => {
return segmentVal !== val.sort().join(',');
})[randomIndex];
}
When dealing with objects rather than arrays and/or arrays/objects that are deeply nested and/or contain non-nested values it devolves into the sticky matter of object equality that is probably not entirely germane to this Q&A
Can't seem to find an answer to this, say I have this:
setInterval(function() {
m = Math.floor(Math.random()*7);
$('.foo:nth-of-type('+m+')').fadeIn(300);
}, 300);
How do I make it so that random number doesn't repeat itself. For example if the random number is 2, I don't want 2 to come out again.
There are a number of ways you could achieve this.
Solution A:
If the range of numbers isn't large (let's say less than 10), you could just keep track of the numbers you've already generated. Then if you generate a duplicate, discard it and generate another number.
Solution B:
Pre-generate the random numbers, store them into an array and then go through the array. You could accomplish this by taking the numbers 1,2,...,n and then shuffle them.
shuffle = function(o) {
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
var randorder = shuffle([0,1,2,3,4,5,6]);
var index = 0;
setInterval(function() {
$('.foo:nth-of-type('+(randorder[index++])+')').fadeIn(300);
}, 300);
Solution C:
Keep track of the numbers available in an array. Randomly pick a number. Remove number from said array.
var randnums = [0,1,2,3,4,5,6];
setInterval(function() {
var m = Math.floor(Math.random()*randnums.length);
$('.foo:nth-of-type('+(randnums[m])+')').fadeIn(300);
randnums = randnums.splice(m,1);
}, 300);
You seem to want a non-repeating random number from 0 to 6, so similar to tskuzzy's answer:
var getRand = (function() {
var nums = [0,1,2,3,4,5,6];
var current = [];
function rand(n) {
return (Math.random() * n)|0;
}
return function() {
if (!current.length) current = nums.slice();
return current.splice(rand(current.length), 1);
}
}());
It will return the numbers 0 to 6 in random order. When each has been drawn once, it will start again.
could you try that,
setInterval(function() {
m = Math.floor(Math.random()*7);
$('.foo:nth-of-type(' + m + ')').fadeIn(300);
}, 300);
I like Neal's answer although this is begging for some recursion. Here it is in java, you'll still get the general idea. Note that you'll hit an infinite loop if you pull out more numbers than MAX, I could have fixed that but left it as is for clarity.
edit: saw neal added a while loop so that works great.
public class RandCheck {
private List<Integer> numbers;
private Random rand;
private int MAX = 100;
public RandCheck(){
numbers = new ArrayList<Integer>();
rand = new Random();
}
public int getRandomNum(){
return getRandomNumRecursive(getRand());
}
private int getRandomNumRecursive(int num){
if(numbers.contains(num)){
return getRandomNumRecursive(getRand());
} else {
return num;
}
}
private int getRand(){
return rand.nextInt(MAX);
}
public static void main(String[] args){
RandCheck randCheck = new RandCheck();
for(int i = 0; i < 100; i++){
System.out.println(randCheck.getRandomNum());
}
}
}
Generally my approach is to make an array containing all of the possible values and to:
Pick a random number <= the size of the array
Remove the chosen element from the array
Repeat steps 1-2 until the array is empty
The resulting set of numbers will contain all of your indices without repetition.
Even better, maybe something like this:
var numArray = [0,1,2,3,4,5,6];
numArray.shuffle();
Then just go through the items because shuffle will have randomized them and pop them off one at a time.
Here's a simple fix, if a little rudimentary:
if(nextNum == lastNum){
if (nextNum == 0){nextNum = 7;}
else {nextNum = nextNum-1;}
}
If the next number is the same as the last simply minus 1 unless the number is 0 (zero) and set it to any other number within your set (I chose 7, the highest index).
I used this method within the cycle function because the only stipulation on selecting a number was that is musn't be the same as the last one.
Not the most elegant or technically gifted solution, but it works :)
Use sets. They were introduced to the specification in ES6. A set is a data structure that represents a collection of unique values, so it cannot include any duplicate values. I needed 6 random, non-repeatable numbers ranging from 1-49. I started with creating a longer set with around 30 digits (if the values repeat the set will have less elements), converted the set to array and then sliced it's first 6 elements. Easy peasy. Set.length is by default undefined and it's useless that's why it's easier to convert it to an array if you need specific length.
let randomSet = new Set();
for (let index = 0; index < 30; index++) {
randomSet.add(Math.floor(Math.random() * 49) + 1)
};
let randomSetToArray = Array.from(randomSet).slice(0,6);
console.log(randomSet);
console.log(randomSetToArray);
An easy way to generate a list of different numbers, no matter the size or number:
function randomNumber(max) {
return Math.floor(Math.random() * max + 1);
}
const list = []
while(list.length < 10 ){
let nbr = randomNumber(500)
if(!list.find(el => el === nbr)) list.push(nbr)
}
console.log("list",list)
I would like to add--
var RecordKeeper = {};
SRandom = function () {
currTimeStamp = new Date().getTime();
if (RecordKeeper.hasOwnProperty(currTimeStamp)) {
RecordKeeper[currTimeStamp] = RecordKeeper[currTimeStamp] + 1;
return currTimeStamp.toString() + RecordKeeper[currTimeStamp];
}
else {
RecordKeeper[currTimeStamp] = 1;
return currTimeStamp.toString() + RecordKeeper[currTimeStamp];
}
}
This uses timestamp (every millisecond) to always generate a unique number.
you can do this. Have a public array of keys that you have used and check against them with this function:
function in_array(needle, haystack)
{
for(var key in haystack)
{
if(needle === haystack[key])
{
return true;
}
}
return false;
}
(function from: javascript function inArray)
So what you can do is:
var done = [];
setInterval(function() {
var m = null;
while(m == null || in_array(m, done)){
m = Math.floor(Math.random()*7);
}
done.push(m);
$('.foo:nth-of-type('+m+')').fadeIn(300);
}, 300);
This code will get stuck after getting all seven numbers so you need to make sure it exists after it fins them all.
I have:
function getRandomInt(min, max){
return Math.floor(Math.random() * (max - min + 1)) + min;
}
But the problem is I want randomise the population of something with elements in an array (so they do not appear in the same order every time in the thing I am populating) so I need to ensure the number returned is unique compared to the other numbers so far.
So instead of:
for(var i = 0; i < myArray.length; i++) {
}
I have:
var i;
var count = 0;
while(count < myArray.length){
count++;
i = getRandomInt(0, myArray.length); // TODO ensure value is unique
// do stuff with myArray[i];
}
It looks like rather than independent uniform random numbers you rather want a random permutation of the set {1, 2, 3, ..., N}. I think there's a shuffle method for arrays that will do that for you.
As requested, here's the code example:
function shuffle(array) {
var top = array.length;
while (top--) {
var current = Math.floor(Math.random() * top);
var tmp = array[current];
array[current] = array[top - 1];
array[top - 1] = tmp;
}
return array;
}
Sometimes the best way to randomize something (say a card deck) is to not shuffle it before pulling it out, but to shuffle it as you pull it out.
Say you have:
var i,
endNum = 51,
array = new Array(52);
for(i = 0; i <= endNum; i++) {
array[i] = i;
}
Then you can write a function like this:
function drawNumber() {
// set index to draw from
var swap,
drawIndex = Math.floor(Math.random() * (endNum+ 1));
// swap the values at the drawn index and at the "end" of the deck
swap = array[drawIndex];
array[drawIndex] = array[endNum];
array[endNum] = swap;
endNum--;
}
Since I decrement the end counter the drawn items will be "discarded" at the end of the stack and the randomize function will only treat the items from 0 to end as viable.
This is a common pattern I've used, I may have adopted it into js incorrectly since the last time I used it was for writing a simple card game in c#. In fact I just looked at it and I had int ____ instead of var ____ lol
If i understand well, you want an array of integers but sorted randomly.
A way to do it is described here
First create a rand function :
function randOrd(){
return (Math.round(Math.random())-0.5); }
Then, randomize your array. The following example shows how:
anyArray = new Array('1','2','3','4','5');
anyArray.sort( randOrd );
document.write('Random : ' + anyArray + '<br />';);
Hope that will help,
Regards,
Max
You can pass in a function to the Array.Sort method. If this function returns a value that is randomly above or below zero then your array will be randomly sorted.
myarray.sort(function() {return 0.5 - Math.random()})
should do the trick for you without you having to worry about whether or not every random number is unique.
No loops and very simple.