Using the same random number in multiple places (JavaScript) - javascript

I'm creating a random quote generator that is supposed to give you a random quote along with the person who said it, but I've created two separate arrays because it'll make it easier to edit the visual aspects of the page later on. The first array has the quote itself and the second array has the names of the people who said them. I would like to generate one random number and then use that as my index on both arrays, thus getting a random quote and the corresponding person who said it.
The first thing I tried was this:
var randomIndex = function() {
return Math.floor(Math.random() * quotes.length);
};
button.addEventListener("click", function(){
quoteBox.textContent = '"' + quotes[randomIndex()] + people[randomIndex()] + '"';
});
But that gives me two different random numbers (because I'm calling the function twice, I suppose). I then tried setting the random number to a variable and using that as the index, but that doesn't change unless I refresh the page and I want it to change on the click of a button. Is what I want to do possible?

Try calling randomIndex inside your click handler.
button.addEventListener("click", function(){
var random = randomIndex();
quoteBox.textContent = '"' + quotes[random] + people[random] + '"';
});
That will assign a new index each time the click handler runs, but will use the same index to access both quotes and people.

Instead of having 2 arrays for quotes and people, use a unique array of objects, each one having 2 members quote and people.
var quotes = [
{quote: 'The quote text', author: 'Its author'},
{quote: 'A second quote text', author: 'The 2nd author'},
// ...
];
button.addEventListener("click", function(){
var quote = quotes[randomIndex()];
quoteBox.textContent = '"' + quote.quote + quote.index + '"';
});

Try this :)
var randomIndex = function() {
return Math.floor(Math.random() * quotes.length);
};
button.addEventListener("click", function(){
var index = randomIndex();
quoteBox.textContent = '"' + quotes[index] + people[index] + '"';
});

I would suggest instead of maintaining two arrays use one that stores objects with key value pairs of what you need to use in the quote. see below.
var quotes = [{
quote: 'something something darkside',
person: 'family guy sith lord'
}, {
quote: 'something something evil',
person: 'family guy sith lord'
}, {
quote: 'blah blah blah',
person: 'foobar'
}];
var
quoteEl = document.querySelector('#quote'),
personEl = document.querySelector('#person'),
button = document.querySelector('#button');
button.addEventListener("click", getQuote);
getQuote();
function getQuote() {
var quote = quotes[randomIndex(quotes.length)];
quoteEl.innerHTML = quote.quote;
personEl.innerHTML = quote.person;
}
function randomIndex(max) {
return Math.floor(Math.random() * max);
};
blockquote {
padding: .5em;
margin: 1em;
background: #eee;
border-left: 1em solid #aaa;
color: rgba(37, 37, 37, .87);
}
#person {
text-decoration: none;
color: rgba(37, 37, 37, .87);
}
#person::before {
content: ' -- ';
}
#person:hover {
text-decoration: underline;
}
<blockquote>
<p id="quote"></p>
<footer>
</footer>
</blockquote>
<button id="button">get a random quote
</button>

Related

How do I make a random quote generator not repeat quotes

I've searched the site for answers but the one's that come up aren't similar/specific to the code that I have written. I don't know how to modify the code so that the quotes don't repeat when the user presses a button to generate another quote.
var quotes = [
{
quote: "\"Don't just exist, live\""
},
{
quote: "\"Try to be a rainbow in someone's cloud\""
},
{
quote: "\"Prove them wrong\""
},
{
quote: "\"Find reasons to smile\""
},
{
quote: "\"You get what you give\""
}
]
var quotebtn = document.getElementById('quote-btn');
var quote = document.querySelector('.quote');
quotebtn.addEventListener('click', () => {
let random = Math.floor(Math.random() * quotes.length);
quote.innerHTML = quotes[random].quote;
})
If the displayed is no longer important and can be deleted. You can do it easily with an array splice.
example:
var quotes = [
{
quote: "\"Don't just exist, live\""
},
{
quote: "\"Try to be a rainbow in someone's cloud\""
},
{
quote: "\"Prove them wrong\""
},
{
quote: "\"Find reasons to smile\""
},
{
quote: "\"You get what you give\""
}
]
var quotebtn = document.getElementById('quote-btn');
var quote = document.querySelector('.quote');
quotebtn.addEventListener('click', () => {
let random = Math.floor(Math.random() * quotes.length);
quote.innerHTML = quotes[random].quote;
quotes.splice(random, 1); // Trim one from the specified index.
})
This should work for you
quotebtn.addEventListener('click', () => {
if(quotes.length) {
let random = Math.floor(Math.random() * quotes.length);
quote.innerHTML = quotes[random].quote;
quotes.splice(random, 1);
} else {
quote.innerHTML = "No more quotes!";
}
})
quotes.splice(random, 1) removes quotes already presented from the array, preventing it from repeating
Once quotes is emptied, the No more quotes! message can be displayed!
You can make another array with the same length as the quotes array and initialize it with zeros. When quote is generated set the new array at the same index as the quote to one which will mean that the quote is already generated. Next time when you generate quote check first if quote is already generated and if it is generate again.
var quotes = [{
quote: "\"Don't just exist, live\""
},{
quote: "\"Try to be a rainbow in someone's cloud\""
},{
quote: "\"Prove them wrong\""
},{
quote: "\"Find reasons to smile\""
},{
quote: "\"You get what you give\""
}];
quotes.sort(() => Math.random() - 0.5);
var quotebtn = document.getElementById('quote-btn');
var quote = document.querySelector('.quote');
var actualQuote = 0;
quotebtn.addEventListener('click', () => {
if(actualQuote>=quotes.length){
quote.innerHTML = "No more quotes";
} else {
quote.innerHTML = quotes[actualQuote % quotes.length].quote;
actualQuote++;
}
});
<button id="quote-btn">Quote</button><br>
<span class="quote"></span>
I just shuffle the quotes at start. At click I display the first, and the next one, etc.
If you want it not to stop after last one just remove the condition and it will cycle trough all. So after the last it will start from the beginning.

Implementing an add and remove function into my class

I have this code below that takes items of a PC and returns a quote, price etc. As of now, the only way that I can add or remove components is by directly modifying the array. Here is the code
class PriceCalc {
Motherboard = 520.99;
RAM = 250.4;
SSD = 500.8;
HDD = 400.66;
Case = 375.5;
Monitor = 600.75;
Keyboard = 100.99;
Mouse = 25.5;
constructor(Obj) {
this.parts = Obj;
this.cost = "$" + Obj.reduce((a, b) => a + this[b], 0).toFixed(2);
this.retail = "$" + (Obj.reduce((a, b) => a + this[b], 0) + Obj.reduce((a, b) => a + this[b], 0) * 1.75).toFixed(2);
this.quote = "Your quote is " + this.retail;
}
}
This is the key values
quote = new PriceCalc(["Motherboard", "RAM", "SSD", "HDD", "Case", "Monitor", "Keyboard", "Mouse"]);
console.log(quote.parts);
console.log(quote.cost);
console.log(quote.retail);
console.log(quote.quote);
What Id like to implement is a code as shown below that I can add within the class and call it to add new parts to the quote or remove existing parts.
function removePart(arr, part) {
return arr.filter(function (ele) {
return ele != part;
});
} var result = removePart(["Motherboard", "RAM", "SSD", "HDD", "Case", "Monitor", "Keyboard", "Mouse"], "Mouse");
console.log(result)
I would like to implement it in a way where I can call this.parts (as stated in the previous code) and either call a new function that pushes new parts to "parts" or call a new function that removes certain parts that are already in the list. I hope what I've described makes sense
Here is s a general example template of what I mean, without the functional code:
class quote {
constructor(/*...*/) {
this.parts = ....
}
add() {
//adds item
}
remove() {
//removes item
}
}
example = new quote(["part1", "part2"])
add(part3)
console.log(example) //returns ["part1" "part2" "part3"]
remove(part1)
console.log(example) //returns ["part2" "part3"]
This should work
class quote {
constructor(initialParts) {
this.parts = initialParts
}
add(part) {
this.parts = [...this.parts, part]
}
remove(part) {
this.parts = this.parts.filter( p => p !== part)
}
}
example = new quote(["part1", "part2"])
example.add('part3')
console.log(example) //returns ["part1" "part2" "part3"]
example.remove('part1')
console.log(example) //returns ["part2" "part3"]
If you want multiple of the same item you can still use an object and store the key as the name of the product and the number of items as the value. simply adjust the functions to account for this.

JavaScript Random Function does not work

All I want to do is have the program select random questions (each question is within a function and fills a blank area in the HTML so all questions appear in the same area) but it does not work. Does anyone have any idea why? I am meant to click correct answer and it executes the correctFunction but nothing happens. Cheers!
var randomFunctions = [
"Question2", "Question3",
"Question4", "Question5",
"Question6", "Question7",
"Question8"
];
var rand = randomFunctions[Math.floor(Math.random() * randomFunctions.length)];
function correctFunction() {
rand();
}
correctFunction()
You must provide actual functions that can be invoked, not strings (which can't). So, your array will wind up holding references to existing functions or the actual functions themselves.
Based on your updated requirement that possible answers should also be shown along with the question, we need to rethink what you are storing in your array. Since a question will have associated possible answers, the best way to store each question with its answers is in an object. So, in the end, we will have an array of objects.
function q1(){ console.log("hello from q1"); }
function q2(){ console.log("hello from q2"); }
function q3(){ console.log("hello from q3"); }
function q4(){ console.log("hello from q4"); }
var randomFunctions = [q1, q2, q3, q4, function(){
console.log("hello from inline anonymous function.");
}];
var rand = randomFunctions[Math.floor(Math.random() * randomFunctions.length)];
rand();
Now, based on your use case, you really don't need or want to store an array of functions, you need to store an array of questions and then have one function that processes that randomly selected question. Like this:
// Get reference to HTML output area
var question = document.getElementById("questionArea");
var answer = document.getElementById("answerArea");
var btn = document.getElementById("btnGetQuestion");
// Set up button click event:
btn.addEventListener("click", showQuestion);
// Set up questions/answers array as an array of objects so that
// the questions and answers can be connected:
var qa = [
{
question:"What is your name?",
answers: ["Bob","Sally", "Mary", "Tim"]
},
{
question:"What is your favorite color?",
answers: ["Red","Green", "Blue", "Orange"]
},
{
question:"What is the average air speed of a laden swallow?",
answers: ["22 mph","18 mph", "17 kmh", "African or European?"]
}
];
// One function to process question:
function showQuestion(){
// Get a random number based on lenght of the questions array
var num = Math.floor(Math.random() * qa.length);
// Get a random object out of the array and extract the question from the object
question.textContent = qa[num].question;
// Loop over all the values in the "answers" object array
// and display them. Build up an output string as well
var html = "<form>";
qa[num].answers.forEach(function(answer, index){
html += "<input type='radio' name='q" + index + "' value='" + answer + "'>" + answer;
});
// close the string and display:
html += "</form>";
answer.innerHTML = html;
}
button {
margin:2em 0;
}
#answerArea {
margin: 1em 0 0 1em;
}
<div id="questionArea"></div>
<div id="answerArea"></div>
<button id="btnGetQuestion">Get A Question</button>

random quote without back to back

I'm trying to pull a random quote from an array. I need to display an initial quote and then get a new one, no same two quotes back to back. Here's what I got.
$(document).ready(function() {
var quoter = [{
quote: "I drink to make other people more interesting.",
author: "Ernest Hemingway"
}, {
quote: "Alcohol may be man's worst enemy, but the bible says love your enemy.",
author: "Frank Sinatra"
}, {
quote: "Reality is an illusion created by a lack of alcohol.",
author: "N.F. Simpson"
},{
quote: "Time is never wasted when you’re wasted all the time.",
author: "Catherine Zandonella"
},{
quote: "I feel bad for people who don’t drink. When they wake up in the morning, that’s as good as they’re going to feel all day.",
author: "Frank Sinatra"
}];
var randomQuote = Math.floor(Math.random() * quoter.length);
//$(function () {
//Set Original Quote
$('#quoteText').text(quoter[randomQuote].quote);
$('#authorText').text(quoter[randomQuote].author);
//});
$('#btnNew').click(function(evt) {
//prevent browser's default action
evt.preventDefault();
//getting a new random number to attach to a quote and setting a limit
var sourceLength = quoter.length;
var randomNumber = Math.floor(Math.random() * sourceLength);
//set a new quote
//while ( randomNumber <= sourceLength ) {
while (randomNumber === randomNumber){
var newQuoteText = quoter[randomNumber].quote;
var newQuoteGenius = quoter[randomNumber].author;
var timeAnimation = 500;
var quoteContainer = $('#quoteContainer');
//fade out animation with callback
quoteContainer.fadeOut(timeAnimation, function() {
//set text values
$('#quoteText').text(newQuoteText);
$('#authorText').text(newQuoteGenius);
//console.log(quoteText,authorText);
//fadein animation.
quoteContainer.fadeIn(timeAnimation);
});
break;
}; //end while loop
}); //end btnNew function
}); //end document ready
I need to do this by using a while loop. I can't figure out how to store the random quote (array value) and then compare it to the new random one to get a different random on if it's the same.
The HTML is here:
<div id="quoteContainer">
<p><span id="quoteText"></span><Br/></p>
—<span id="authorText"> </span>
</div>
You can have a simple variable store the previous value, then check to see if the random number is the same as the last time.
$(document).ready(function(){
//....your current above code
var lastQuote = randomQuote;
$(button).click(function(){
var thisQuote = Math.floor(Math.random() * sourceLength);
//This will only be entered if the two are equal
//The while loop ensures that if you get a new random number
//it won't be the same
while(thisQuote == lastQuote){
thisQuote = Math.floor(Math.random() * sourceLength);
}
//If you make it here a unique number has been found
lastQuote = thisQuote;
});
});
Your approach might result in an infinite loop. Won't happen, because the random-number generator is not perfect but it can be done easier:
Either shuffel the array and go down linearly (or construct a fancy generator if you want) or just delete every quote that has been shown already.

Search array for string - returning -1

I've been reading lots of StackOverflow answers which tell me that, in Javascript, the best way to search an array for a particular string is use indexOf(). I have been trying to make this work for a while now, and I need some help with it.
I am making a shop in a text-adventure game. These are the values I am using:
The array shopCosts:
shopCosts = [20, 25];
The array shopItems:
shopItems = [["Sword", "Shield"]];
I dynamically create radiobuttons by looping through shopItems:
for(var i = 0; i < array.length; i++)
{
// Create the list item:
var item = document.createElement('li');
// Set its contents:
item.appendChild(document.createTextNode(array[i] + " - " + shopCosts[i] + " Gold"));
// Add it to the list:
list.appendChild(item);
var label = document.createElement("label");
var radio = document.createElement("input");
var text = document.createTextNode(array[i]);
radio.type = "radio";
radio.name = "shop";
radio.value = array[i];
radio.onclick = function () { addValue(this.getAttribute("value"), shopCosts, shopItems) }
label.appendChild(radio);
label.appendChild(text);
document.body.appendChild(label);
}
This is the part in question:
radio.onclick = function () { addValue(this.getAttribute("value"), shopCosts, shopItems) }
My logic was basically to assign values to each dynamically created radiobutton, and if one was pressed, get the value (so, the name of the item you wanted to buy) and then search shopItems for that particular string for the index value. Once I had that, I would look in the same "parallel" list shopCosts to find the price.
I used console.log() to see what variables were in play. When I clicked on the radio button, this function is called:
function addValue(nameOfItem, shopCosts, shopItems)
{
var positionOfShopItem = shopItems.indexOf(nameOfItem);
console.log(positionOfShopItem);
console..log(nameOfItem);
console.log(shopItems);
}
Surely, the console.log() would return the position of the named item? To prove to myself I'm not going crazy, here's what the Dev Tools say:
-1
Sword
[Array[2]]
0: "Sword"
1: "Shield"
Sword is clearly in the array, in position 0, so why is indexOf() returning -1?
Any help appreciated!
As I alluded to in my comment, its because shopItems does not contain an array of strings, it contains a single element, where that one element is an array of strings. I suspect your code would work just fine if you removed the extra square braces
var shopItems = ["Sword", "Shield"];
I realize you've already fixed the bug, but I urge you to consider a different approach to the problem. These two principles will not only solve the problem in a cleaner way, but they also give you a new way to think about similar problems in the future:
Never use parallel arrays. Use a single array of objects instead.
In your main loop that appends the items, put the main body of the loop in a function.
If you follow these two ideas you gain several benefits. The code becomes much more straightforward, easier to maintain, and you don't have to do any array lookups at all!
Each shop item is packaged up as a single object in the array, like this:
var shopItems = [
{ name: 'Sword', cost: 20 },
{ name: 'Shield', cost: 25 }
];
So if you have a reference to the shop item as a whole, say in a variable called shopItem, then you automatically have all of its properties available: shopItem.name and shopItem.cost. This lets you also easily add more bits of data to a shop item, e.g.
var shopItems = [
{ name: 'Sword', cost: 20, dangerous: true },
{ name: 'Shield', cost: 25, dangerous: false }
];
and now shopItem.dangerous will give you the appropriate value. All without any array lookups.
Making the main loop body into a function adds a further benefit: Inside that function, its parameters and local variables are preserved each time you call the function (this is called a closure). So now you don't even have to fetch the list item value and look it up - you already have the appropriate shopItem available in the code.
Putting this together, the code might look like this:
var shopItems = [
{ name: 'Sword', cost: 20, dangerous: true },
{ name: 'Shield', cost: 25, dangerous: false }
];
var list = document.getElementById( 'list' );
for( var i = 0; i < shopItems.length; ++i ) {
appendShopItem( shopItems[i] );
}
// Alternatively, you could use .forEach() instead of the for loop.
// This will work in all browsers except very old versions of IE:
// shopItems.forEach( appendShopItem );
function appendShopItem( shopItem ) {
// Create the list item:
var item = document.createElement( 'li' );
// Set its contents:
item.appendChild( document.createTextNode(
shopItem.name + ' - ' + shopItem.cost + ' Gold'
) );
// Add it to the list:
list.appendChild( item );
var label = document.createElement( 'label' );
var radio = document.createElement( 'input' );
var text = document.createTextNode( shopItem.name );
radio.type = 'radio';
radio.name = 'shop';
radio.value = shopItem.name;
radio.onclick = function () {
addValue( shopItem );
};
label.appendChild( radio );
label.appendChild( text );
document.body.appendChild( label );
}
function addValue( shopItem ) {
console.log( shopItem );
alert(
shopItem.name +
' costs ' + shopItem.cost + ' and is ' +
( shopItem.dangerous ? 'dangerous' : 'not dangerous' )
);
}
New fiddle (with a tip of the hat to Jamiec for the original fiddle)
As you can see, this makes the code much easier to understand. If you have a shopItem, you automatically have its name, cost, and any other property you want to add. And most importantly, you never have to keep track of putting your values in the same order in two, three, or even more different arrays.
shopItems is an Array of Arrays. The 0 index of shopItems contains another array which contains:
["Sword", "Shield"]
So when you are trying to find the "Sword" item or "Shield" Item inside of shopItems it is returning -1 because it cannot find either inside of the array.
Change
shopItems = [["Sword", "Shield"]];
To
shopItems = ["Sword", "Shield"];
And that will fix your issue.
I've fixed it!
Removing the double square brackets resulted in this mess. So, as a workaround, I simply added [0] to var positionOfShopItem = shopItems.indexOf(nameOfItem); to get var positionOfShopItem = shopItems[0].indexOf(nameOfItem);
Thanks for everyone's help.

Categories

Resources