Understanding Private Variables (Closure?) in Javascript with specific example - javascript

OK, I have tried to use a closure to no avail on the following code to keep a variable private. I am brand new to javascript and have read a number of posts about closures and can still not wrap my head around them. Below, I have a function that, upon each press of a particular button, displays the next word in an array. I want my counter variable ("whatNumber" below) that I am using in this function to not be global but I cannot figure out how. Here is my simple code:
var wordList = ["jumper", "stumpy", "smelly gumdrops", "awesome puttputt", "soilent green"];
var whatNumber = 0;
function changeWord(){
if (whatNumber < wordList.length) {
alert(wordList[whatNumber]);
whatNumber++;
}
};

function changeWord(){
var wordList = ["jumper", "stumpy", "smelly gumdrops", "awesome puttputt", "soilent green"];
var whatNumber = 0;
return function alertWord(){
if (whatNumber < wordList.length) {
alert(wordList[whatNumber]);
whatNumber++;
}
}
};
//to run this
var alertNewWord = changeWord();
alertNewWord() //jumper
alertNewWord() //stumpy
This comes with a bonus of being able to have different functions having different levels of alerting. e.g: if you do another var anotherAlertFn = changeWord() and you call anotherAlertFn() it will result in "jumper". The initial functions (i.e: alertNewWord()) will still have it's own state, i.e: whatNumber === 3 while anotherAlertFn has whatNumber === 1. This can be very useful, imagine a function keeping score for different players in a game. Every player can use the same function without being able to cheat (i.e: change their score) and never affecting other players' scores.

Related

I am trying to stop my function from displaying the same object twice when clicking a button

I have for quite some time now been trying to figure out how I can stop my code to print the same quote twice.
Also, when every single object in the array has been printed out, I'd like for it to reset somehow. So that you can browse through the quotes once you've gone through all of them.
This is the essential parts of my code:
document.getElementById('loadQuote').addEventListener("click", printQuote, false);
The printQuote function simply contains information that's accessing information from my array:
var randomObjectNumber = getRandomQuote();
var html = "<p class='quote'>"
+ quotes[randomObjectNumber].quote +
"</p>";
document.getElementById('quote-box').innerHTML = html;
One random object is displayed each time you click the eventListener:
function getRandomQuote () {
var randomObjectNumber = Math.floor(Math.random() * quotes.length );
return randomObjectNumber;
}
I have some ideas on how to do this and I have tried them but without success. I tried giving each object a boolean property but I can't really seem to assign each property a boolean value without messing the printQuote function up.
I also tried assigning the object displayed to a different array but the same problem occurred there.
I feel like there is some concepts around the eventListener that I don't fully understand, because every time I try to manipulate a displayed object I just end up changing every single object.
This is what a typical object in the array looks like by the way:
{quote : "Darkness is merely the absence of light"}
(I also have other properties assigned to the object but i feel like presenting them would be redundant)
If someone could explain, or give me a hint, on how to solve this problem I've been struggling with for some time.
Some hints would be greatly appreciated!
Have a nice day.
Sebastian.
EDIT: All code: https://jsfiddle.net/fusqb7hz/
Basically what you need:
Create a separate array that will store all quotes that you've already used.
Remove quote from initial array.
Check if you still have quotes in initial array, if not, get them back from backup array.
The problem is that you call addEventListener twice:
//Let's developers create multiple eventListeners without being redundant.
function onClicking (printFunction) {
document.getElementById('loadQuote').addEventListener("click", printFunction, false);
}
onClicking(printColor);
onClicking(printQuote);
by calling onClicking twice you make the click happen twice, so addEventListener is added twice, meaning one click counts as two.
Change the above code for this:
//Let's developers create multiple eventListeners without being redundant.
document.getElementById('loadQuote').addEventListener("click", function(){
printColor();
printQuote();
});
Here is the jsfiddle:
https://jsfiddle.net/fusqb7hz/3/
I think the easiest approach is to shuffle your quote array and then go through them one by one. This gives you the next "random" as yet unseen quote. The only part I'm not keen on is this shuffler (a derivation of Fisher Yates) modifies the original quote array. You might not care about that though.
// --------------------------------
// A bunch of quotes
// --------------------------------
var quotes = [];
quotes.push({quote : "Darkness is merely the absence of light"});
quotes.push({quote : "quote 2"});
quotes.push({quote : "quote 3"});
quotes.push({quote : "quote 4"});
quotes.push({quote : "quote 5"});
// --------------------------------
// --------------------------------
// Your favorite array shuffle utility
// --------------------------------
var shuffle = function(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
// --------------------------------
// --------------------------------
// construct a function to get a random unseen quote until
// all quotes have been seen. Then reset...
// --------------------------------
var getQuote = (function(quotes, shuffle){
var current = 0;
var get = function(){
if ( !quotes || !quotes.length ) { return ""; }
if ( current >= quotes.length ){ current = 0; }
if ( current === 0 ){
console.log("randomizing quotes...");
shuffle(quotes);
}
return quotes[current++].quote;
};
return get;
})(quotes, shuffle);
// --------------------------------
var printQuote = function(){
document.getElementById('quote').innerText = getQuote();
};
document.getElementById('loadQuote').addEventListener("click", printQuote, false);
<div id="quote"></div>
<button id="loadQuote">get quote</button>

How to you use recursion in javascript to create key value objects

I understand how to go about tasks using loops, recursion is kind of a mystery to me, but from what I understand in certain cases it can save a ton of time if looping through a lot of data.
I created the following function to loop through a large(ish) data set.
var quotes = require('./quotes.js');
//Pulls in the exported function from quotes.js
var exportedQuotes = quotes.allQuotes();
var allAuthors = exportedQuotes.author;
//Create an empty key value object, we use these to coerce unique values to an array
var uniqs = {};
//I create this object to hold all the authors and their quotes
var fullQuote = {};
//Create an object with only unique authors
for(var i = 0; i < allAuthors.length ; i++){
fullQuote[allAuthors[i]] = null;
}
//Coerce unique authors from javascript object into an array
var uniqAuthors = Object.keys(uniqs);
var quoteCount = exportedQuotes.author.length;
var iterativeSolution = function(){
for(var i = 0; i < Object.keys(fullQuote).length; i++){
for(var j = 0; j < exportedQuotes.author.length; j++){
//If the author in the unique list is equal to the author in the duplicate list
if(Object.keys(fullQuote)[i] == exportedQuotes.author[j]){
//if an author has not had a quote attributed to its name
if(fullQuote[exportedQuotes.author[j]] == null){
//assign the author an array with the current quote at the 0 index
fullQuote[exportedQuotes.author[j]] = [exportedQuotes.quote[j]]
} else {
//if an author already has a quote assigned to its name then just add the current quote to the authors quote list
fullQuote[exportedQuotes.author[j]].push(exportedQuotes.quote[j])
}
}
}
}
}
I don't currently have the skills to do analyze this, but, I'm wondering if there is a case for recursion to save the time it takes to get through all the loops. And if there is a case for recursion what does it look like for nested loops in javascript, specifically when creating key value objects recursively?
There may be a slight misunderstanding about what recursion is: recursion does not save time. It's just a different way of doing the same traversal. It generally a little easier to read, and depending on the problem, will map to certain algorithms better. However, one of the first things we do when we need to start optimizing code for speed is to remove recursion, turning them back into loops, and then even "unrolling" loops, making code much uglier, but fast, in the process. Recursion vs plain loops is almost always a matter of taste. One looks nicer, but that's hardly the only quality we should judge code on.
And also: just because it sounds like I'm advocating against using it, doesn't mean you shouldn't just try it: take that code, put it in a new file, rewrite that file so that it uses recursion. Doing so lets you compare your code. Which one is faster? Which is easier to read? Now you know something about how (your) code behaves, and you'll have learned something valuable.
Also don't sell yourself short: if you wrote this code, you know how it works, so you know how to analyze it enough to rewrite it.
Algorithms makes code fast or slow, not recursion. Some quite fast algorithms can use recursion, but that's a whole different story. Many algorithms can be written as both with recursion, and without recursion.
However, your code has a big problem. Notice how many times you call this code?
Object.keys(fullQuote)
You are re-computing the value of that many many times in your code. Don't do that. Just call it once and store in a variable, like the following:
var uniqAuthors = Object.keys(uniqs);
var uniqFullQuote = Object.keys(fullQuote);
var quoteCount = exportedQuotes.author.length;
//Loop through all quotes and assign all quotes to a unique author::Each author has many quotes
for(var i = 0; i < uniqFullQuote.length; i++){
for(var j = 0; j < exportedQuotes.author.length; j++){
//If the author in the unique list is equal to the author in the duplicate list
if(uniqFullQuote[i] == exportedQuotes.author[j]){
//if an author has not had a quote attributed to its name
if(fullQuote[exportedQuotes.author[j]] == null){
//assign the author an array with the current quote at the 0 index
fullQuote[exportedQuotes.author[j]] = [exportedQuotes.quote[j]]
} else {
//if an author already has a quote assigned to its name then just add the current quote to the authors quote list
fullQuote[exportedQuotes.author[j]].push(exportedQuotes.quote[j])
}
}
}
}
You don't have to iterate Object.keys(fullQuote).
var quotes = require('./quotes.js'),
exportedQuotes = quotes.allQuotes(),
allAuthors = exportedQuotes.author,
fullQuote = Object.create(null);
for(var i=0; i < allAuthors.length; ++i)
(fullQuote[allAuthors[i]] = fullQuote[allAuthors[i]] || [])
.push(exportedQuotes.quote[i])
I don't recommend recursion. It won't improve the asymptotic cost, and in JS calling functions is a bit expensive.
I got really curious and created a recursive solution just to see how it works. Then timed it, my iterative solution took 53 seconds to run, while my recursive solution took 1 millisecond to run. The iterative approach can obviously be tweaked based on the answers provided below, to run faster, but a recursive approach forced me to think in a "leaner" manner when creating my function.
var exportedQuotes = quotes.allQuotes();
var allAuthors = exportedQuotes.author;
var n = allAuthors.length
var fullQuote = {};
var recursiveSolution = function(arrayLength) {
//base case
if(arrayLength <= 1){
if(fullQuote[exportedQuotes.author[0]] == null){
fullQuote[exportedQuotes.author[0]] = [exportedQuotes.quote[0]];
}else{
fullQuote[exportedQuotes.author[0]].push(exportedQuotes.quote[0])
}
return;
};
//recursive step
if(fullQuote[exportedQuotes.author[arrayLength]] == null){
fullQuote[exportedQuotes.author[arrayLength]] = [exportedQuotes.quote[arrayLength]];
}else{
fullQuote[exportedQuotes.author[arrayLength]].push(exportedQuotes.quote[arrayLength])
}
newLength = arrayLength - 1;
return recursiveSolution(newLength);
}
////////Timing functions
var timeIteration = function(){
console.time(iterativeSolution);
iterativeSolution(); // run whatever needs to be timed in between the statements
return console.timeEnd(iterativeSolution);
}
var timeRecursive = function(){
console.time(recursiveSolution(n));
recursiveSolution(n); // run whatever needs to be timed in between the statements
return console.timeEnd(recursiveSolution(n));
}

Creating an array and storing it in sessionStorage with JavaScript?

I'm doing an assignment that requires us to add objects to a fake cart array from a fake database array, then go to a cart page that displays everything in the "cart." Now, that's all well and good, but for some reason I can't get more than one object to show up in the fakeCart array.
I'm fairly certain the issue is in this function, because everything displays properly otherwise in every way.
So, it turns out I posted code that I was tinkering with. I've since updated it to the almost-working one.
function addToCart(e) {
'use strict';
var fakeCart = [];
for (var i = 0; i < fakeDatabase.length; i++) {
if (fakeDatabase[i].id == e.currentTarget.id) {
fakeCart.push(fakeDatabase[i]);
}
}
sessionStorage.fakeCart = JSON.stringify(fakeCart);
}
Essentially, I can get the code to make a single object go from one array (database) to the other (cart), but whenever I try to add one back in it just replaces the last one.
The code overwrites any existing value of sessionStorage.fakeCart, so there will never be more than one element in the serialized array. You can fix that by reading the value from sessionStorage instead of creating a new list each time.
function addToCart(e, productNum) {
'use strict';
// change this
var fakeCart = JSON.parse(sessionStorage.fakeCart) || [];
for (var i = 0; i < fakeDatabase.length; i++) {
if (fakeDatabase[i].id == e.currentTarget.id) {
// and this
fakeCart.push(fakeDatabase[i]);
}
}
sessionStorage.fakeCart = JSON.stringify(fakeCart);
}
I think :
Instead of
fakeCart[i].push(fakeDatabase[i]);
you require this
fakeCart.splice(i, 0, fakeDatabase[i]);

Passing array of objects to a js function

i am trying for the first time to implement OOP in javascript and i got stuck on a recursive function when i try to send an array of objects to this function. So, i have the "Pitic" class (pitic means midget in romanian) with some propreties:
function Pitic(piticID) {
this.id = piticID;
this.inaltime = null;
this.greutate = null;
this.genereazaGreutate();
this.genereazaInaltime();
}
I'm now generating some midgets and storing them in the public piticiCollection Array variable. The "genereazaGreutate" and "genereazaInaltime" are function to generate random values for the inaltime and greutate values.
var pitic = new Pitic(idPitic);
piticiCollection.push(pitic);
The problem appears when i try to send the array of midgets to a function because all i get is only the first item of the array.
So, before i call the function, i have piticiCollection array with 4 objects:
midgets are safe and sound http://img443.imageshack.us/img443/484/yr4f.png
And as soon as i call the function with the piticiCollection as a parameter i loose 3 midgets! :(
most of the midgets are gone http://img201.imageshack.us/img201/5808/7od5.png
p.s. please excuse me for my bad english..
[EDIT]
Here is a fiddle of my full code: http://jsfiddle.net/WT7Ud/ I call the function on line 56 and as soon as the debugger hits line 60 i loose array items.
I have solved my problem by creating a copy of the array before using it in the function. Strange :(
function determinaPerechi(somePitici) {
var piticDeComparat, colectieDePiticiCopy;
colectieDePiticiCopy = somePitici;
for (var i = 1; i < colectieDePiticiCopy.length; i++) {
var piticDeComparat2 = null;
piticDeComparat = colectieDePiticiCopy[0];
piticDeComparat2 = colectieDePiticiCopy[i];
if (piticDeComparat.inaltime < piticDeComparat2.inaltime) {
//Perechea poate fi prietena
}
}
//colectieDePiticiCopy.splice(0, 1);
if (colectieDePiticiCopy.length == 0) {
//alert("finish");
return;
}
determinaPerechi(colectieDePiticiCopy);
//test(ttt);
}
Your determinaPerechiPosibile is modifying the original array on this line:
colectieDePitici.splice(1, colectieDePitici.length);
In particular, it is removing all but the first element. You probably should be using slice to non-destructively extract the part of the array you want to recurse on.
As Ted Hopp mentioned, the problem appears to be the line
colectieDePitici.splice(1, colectieDePitici.length);
in combination with this line:
determinaPerechiPosibile(colectieDePiticiCopy);
If those two lines are commented out, the array maintains its original length.

Store a table row index as an array index

There a simple function:
selected_row = []; // global scope
function toggleRowNumber(rowIndex) {
if(selected_row[rowIndex]) selected_row.splice(rowIndex, 1);
else selected_row[rowIndex] = 1;
}
usage
toggleRowNumber(50000); // click the row - write the index
toggleRowNumber(50000); // click the row again - remove the inxed
alert(selected_row.length);
50001
OK
Delightful feature!
So is there a way to direct write|read an index without any searchin/looping? And without this huge feat as decribed above.
Thanks.
If I understoold correctly, you want to store and index where you can check/set whether an item is selected or not. If that is the case, you are looking for a "key - value" data structure. Then, why not use a map?
var selected_row = {};
function toggleRowNumber(rowIndex) {
if(selected_row[rowIndex]) selected_row[rowIndex] = 0; //or = undefined;
else selected_row[rowIndex] = 1;
}
That is better because hash map will save you time and space.
Space becuase you are not storing hundreds of 'undefined' values in a vector.
Time because, hash function used to access elements is pretended to hit the right position in many cases.

Categories