Free the counter variable when a while loop exits in JavaScript - javascript

I've found that when making masses of objects in JavaScript, the while loop is the best tool to tune performance.
I get the best speed when writing my loop like this:
var i = array.length
while (i--) {
// Do stuff
}
However, if I want to nest a while loop, I have to use a different variable name, otherwise the counter breaks:
var i = array1.length
while (i--) {
var i = array2.length
while (i--) {
// NOPE THE COUNTER IS NOW BROKEN
}
}
Some have suggested j, but then why not start the first array with a variable called a and go up form there?
What's the best practice in this situation?
Is there a way to delete the variable so it isn't available in the secondary scope?

The use of i and j (followed by k if you are nesting that deep) are standard idioms shared by many programming languages. (In fact, the use of i as a loop variable goes back to ForTran.)
By using i and j, anyone reading your code should realize those are standard loops and loop counters. Using a makes the reader stop and ponder over "Why was that used?"
Now, to answer your question:
In Javascript, you could reuse the variable i if you wanted to by using an anonymous function - but this is
Not standard practice
An anti-pattern -- AKA dumb
Slower to execute.
--
var i = 10;
while (i--) {
(function() {
// New function, this is a new `i`, completely masking the original.
var i = 20;
while(i--) {
}
{();
}

You need variable scope here. In C, you can create variable scope with block:
int i = 1;
{
int i = 2;
printf("inner i = %d\n", i);
}
printf("outer i = %d\n", i);
//=>
inner i = 2
outer i = 1
However, in javascript, block doesn't create new variable scope, but shares the outer one. You'll need function to create a new variable scope.
var i = 2;
while (i--) {
console.log("outer i = " + i);
(function () {
var i = 3;
while (i--) {
console.log("inner i = " + i);
}
})();
}
//=>
outer i = 1
inner i = 2
inner i = 1
inner i = 0
outer i = 0
inner i = 2
inner i = 1
inner i = 0

Related

How do I loop through an array of objects and fill the object in the mouseover function?

I'm a first year student I've been scrolling through Stack Overflow and have read a lot about the object problem (reference) but I can't figure out the solution to my problem.
I have made arrays of objects and looping over them to fill a div with all the info like img, name, value, so far no problem here.
The problem is with filling a mouseover function (attached to the image) with the object I'm looping through at the moment, so later when I hover over the image all the info of that particular object is shown on another div.
for (i = 0; i < arrgezelschap.lenght; i++) {
var x = arrgezelschap[i];
var element = document.createElement("img");
element.src = x.artikelFoto + "k.jpg";
element.addEventListener('mouseover', function() {
showinfo(x)
});
inhoud.append(element);
}
In the function showinfo(object) the output is always the last object of the array.
Why is this and what do I need to do so that it saves or points to the object that it's looping through at the moment in my function?
TL;DR: change var x to let x
I can't really do a better job explaining than Creating closures in loops: A common mistake, but I'll take a shot at rephrasing it.
Compare the output of these two snippets (below). The only difference is var vs let. The example demonstrates creating 5 functions in a loop, but does not call them yet. Each function references variables declared inside the loop, outside the loop and in the for itself. Then, at the end of the loop, we call all the functions to see what we got.
In the first case, the variables outside, i (the loop variable) and inside (declared inside the loop) are all declared with var. They are the same variable on every iteration of the loop. The inside var is hoisted to the top of the scope (outside the loop).
When we call all the functions we created, we will see that they all refer to the one-and-only instance of each variable, and they all have the value that the variables have after completion of the loop.
let functions = [];
var outside = 0;
for (var i = 0; i < 5; ++i) {
outside = i * 10;
var inside = i * 100;
functions.push(() => { console.log(outside, i, inside); })
}
functions.map(f => f()); // call all the functions
Output:
40 5 400
40 5 400
40 5 400
40 5 400
40 5 400
In this second example, the variables are all declared with let. The variable i declared in the for and the variable inside declared inside the body of the loop are different variables on each iteration of the loop. But the outside variable is declared outside the loop, so there's still only one outside variable that is used in every iteration of the loop.
When we call all the functions we made this time, we see that each function is displaying a different variable i and inside and their values are the value they held during that particular iteration of the loop, because the variables only existed for that iteration of the loop and the function was bound to the instance of the variable that was used for that iteration. But the outside variable is the same variable every iteration and holds only one value: the value that it has at the end of the loop.
let functions = [];
let outside = 0;
for (let i = 0; i < 5; ++i) {
outside = i * 10;
let inside = i * 100;
functions.push(() => { console.log(outside, i, inside); })
}
functions.map(f => f()); // call all the functions
Output:
40 0 0
40 1 100
40 2 200
40 3 300
40 4 400
In your case, each function binds to the same (one and only) variable x. If you change your declaration of x from var x to let x then you will get a different variable x for each iteration of the loop, and the event listener function will be bound to a different x each time, which will have the value corresponding to that iteration of the loop.
Footnote: Hopefully functions.map(f => f()); is not confusing for you. It just calls all the functions in the array. It is the same as this:
for (var index = 0; index < functions.length; ++index) {
functions[index]();
}
This is because x is a reference here, not a value and it change while you loop. Have a look at this :
let x = 0;
let fcn = a => console.log(a);
function execAnotherFcn(fcn) {
fcn(x);
}
execAnotherFcn(fcn);
x++;
execAnotherFcn(fcn);
You could use the dataset attribute to store your information.
Here's my implementation:
const root = document.querySelector('#root');
function createImagePlaceholder(color, data) {
const el = document.createElement('div');
el.style.width = '50px';
el.style.height = '50px';
el.style.margin = '5px';
el.style.backgroundColor = color;
el.dataset = data;
root.appendChild(el);
el.addEventListener('mouseover', () => {
document.querySelector('pre').innerText = JSON.stringify(data);
});
el.addEventListener('mouseleave', () => {
document.querySelector('pre').innerText = '';
});
}
createImagePlaceholder('red', { text: 'I am a red block' });
createImagePlaceholder('blue', { text: 'I am a blue block' });
<div id="root"></div>
<pre><pre>
You can fix this by making the scope of element block level.
This happens because here the value of x is send as a closure and the var is defined as function level.The event listner function will get executed at a future time(not to the main thread), so at that time the value of x is changed by the loop to the last value.
This can be done using the let key word or using a IIFE.
1.
for (i = 0; i < arrgezelschap.length; i++) {
let x = arrgezelschap[i];
let element = document.createElement("img");
element.src = x.artikelFoto + "k.jpg";
element.addEventListener('mouseover', function() {
showinfo(x)
});
inhoud.append(element);
}
2.
for (i = 0; i < arrgezelschap.lenght; i++) {
var x = arrgezelschap[i];
var element = document.createElement("img");
element.src = x.artikelFoto + "k.jpg";
(function(x){element.addEventListener('mouseover', function() {
showinfo(x)
});})(x);
inhoud.append(element);
}
#PopHips answer explains the theory of what is going wrong. so here is a working example with your code so you can follow it.
for(i =0;i<arrgezelschap.lenght;i++){
var x = arrgezelschap[i];
var element = document.createElement("img");
element.src = x.artikelFoto + "k.jpg";
element.dataset.identifyer = i;
element.addEventListener('mouseover', function(e) {
showinfo(arrgezelschap[e.target.dataset.identifyer])
});
inhoud.append(element);
}
So because we're using an event listener it will give the first param as an EventArgs object, this contains a property called target that is the HTMLElement effected. we can use the dataset (data-) system to save the identifier to the object's dataset so we can use it in the event handler.
Please note this answer should not be used as it is, there is some really bad practice in this answer, NEVER CREATE A FUNCTION INSIDE A LOOP in production code.

Strange behaviour of for loop in Heap's algorithm in JavaScript

I'm trying to implement Heap's algorithm to find different permutations of a string and found a strange behaviour with a for loop, here's the code
function permAlone(str) {
var strArr = str.split(''),
permutations = [];
function swap(strArr, x, y) {
var tmp = strArr[x];
strArr[x] = strArr[y];
strArr[y] = tmp;
}
function generate(n) {
if (n === 1) {
permutations.push(strArr.join());
} else {
for (var i = 0; i != n; i++) {
generate(n - 1);
swap(n % 2 ? 0 : i, n - 1);
}
}
}
generate(strArr.length);
return permutations;
}
console.log(permAlone('aab'));
In the for loop within the generate function, if I put i = 0 the output of the script is ['a,a,b', 'a,a,b'] but if I put var i = 0 the output is ['a,a,b', 'a,a,b', 'a,a,b', 'a,a,b', 'a,a,b', 'a,a,b'].
I understand that var i would create a local variable for the loop, but don't understand in this case why it would change how the loop functions as there is no i variable declared anywhere else in the script.
Thanks any help appreciated.
The reason the behaviour changes if you have a global i variable is that you have multiple recursive calls to generate() all trying to control their own partially complete for loops with the same variable, and all setting i back to 0 when they start.
Picture what happens on the second iteration of the for loop: i is 1 because it has just been incremented, but then immediately a recursive call to generate() starts its own loop and sets i back to 0 again.
If you create a local variable with var then each for loop in each recursive call is independent of all the others.
Try stepping through the code with the debugger, or try adding the following as the first line inside the for loop and watch what happens to the variables when the code runs:
console.log('n:' + n + '; i: '+i);

Is it possble to do comparion of two variables in javascript of different types

I am having a jstree and in javascript function i am checking which node has been selected thats working fine. But when i assign that node value to a variable then do comparasion with a normal string variable thats not working.
$(document).ready(function() {
$('#bdeViewNew').on('changed.jstree', function(e, data) {
var i, j, r = [];
for (i = 0, j = data.selected.length; i < j; i++) {
var x = data.instance.get_node(data.selected[i]).text;
r.push(data.instance.get_node(data.selected[i]).text);
if(x=="Hadoop")
{alert("hi");}
else{
alert("hello");
}
}
});
});
any one know how we can do such comparasion?
thanks in advance
It is possible that your variable has some unexpected whitespace at the start or the end, that's a common problem. You should be able to get rid of it with
var x = data.instance.get_node(data.selected[i]).text.trim();
Should work on all modern browsers.
Also keep in mind that JavaScript has function scope, not block scope like you might be used to from C/C++ or Java. The x you declare there is visible throughout the function, not just in the for loop. You should probably declare it at the same time as i, j and r.

Eloquent Javascript 2nd. Chapter 4. Computing Correlation. Final Analysis - [Variable Recursion & in operator] Pt 2

I'm working through the end of the first example in Chapter 4 Eloquent Javascript. Here is the full piece of code (It's the last piece that I have questions regarding but I attached the first portion for reference).
var journal = [];
function addEntry(events, didITurnIntoASquirrel) {
journal.push({
events: events,
squirrel: didITurnIntoASquirrel
});
function phi(table) {
return (table[3] * table[0] - table[2] * table[1]) /
Math.sqrt((table[2] + table[3]) *
(table[0] + table[1]) *
(table[1] + table[3]) *
(table[0] + table[2]));
}
function hasEvent(event, entry) {
return entry.events.indexOf(event) != -1;
}
function tableFor(event, journal) {
var table = [0, 0, 0, 0];
for (var i = 0; i < journal.length; i++) {
var entry = journal[i], index = 0;
if (hasEvent(event, entry)) index += 1;
if (entry.squirrel) index += 2;
table[index] += 1;
}
return table;
}
function gatherCorrelations(journal) {
var phis = {};
for (var entry = 0; entry < journal.length; entry++) {
var events = journal[entry].events;
for (var i = 0; i < events.length; i++) {
var event = events[i];
if (!(event in phis))
phis[event] = phi(tableFor(event, journal));
}
}
return phis;
}
var correlations = gatherCorrelations(JOURNAL);
console.log(correlations.pizza);
My questions are:
What is the purpose of the .events in
var events = journal[entry].events;
Does it call on itself as a recursion? If so why? Couldn't we have just had journal[entry] and the function would run calling on the entry from the tableFor function? Does it call back to the AddEntry function (where the events variable was established) in an important way?
What is the purpose of (!(event in phis)).
I read it as : if event in phis is true then flip it to be not true and then trigger necessary phi calculation. Wouldn't it make more sense to eliminate the ! (does not equal) or that piece of code altogether? If we already have a for loop won't the function run on it's on until the max length of journal and stop?
var events = journal[entry].events; you are getting the events object from the object at index entry in the array journal and assigning it to a temporary variable called events.
This is just done for convenience so you don't have to keep referring to journal[entry].events. For example, later on it has this line:
var event = events[i];
which would become:
var event = journal[entry].events[i];
Without the assignment to a temporary variable.
if(!(event in phis)) it's testing to see if the object phis does not have a property with the name contained in the variable event. If the object doesn't already have that property, it adds it with the next line:
phis[event] = phi(tableFor(event, journal));
See in operator
Here's a very simple snippet of code to help understand the in operator:
var foo = {};
console.log("bar" in foo); // logs "false" because foo doesn't have a "bar" property
foo.bar = 1; // or foo["bar"] = 1;
console.log("bar" in foo); // logs "true" because foo now has a "bar" property
What is the purpose of the .events in
var events = journal[entry].events;
The purpose of this declaration conforms with 2 concepts of scripting convenience and economy. Every level of an object depth adds to the overall recall time when called. Certainly modern browsers have done much to flatten an objects variable stack, but if you think about it logically, any call to say object1.object2["Memeber3"].object4 has to go through 4 steps to get to the value itself. Flattening the value to a local object is more economic. Second, the readability (and therefore maintainability) of the code is enhanced in that you don't have the extra "journal[entry]." clogging up your expressions. As a general rule of thumb, if you are going to use a member of an object more than once in a block, then you should create a local variable of that member.
What is the purpose of
(!(event in phis)).
The purpose of this evaluation is to determine if a particular member(event) is NOT in the object(phis). In this example, the next line creates that member if it is indeed missing.

Javascript returns negative number for index

Getting the negative value when i perform the onclick function in javascript
function sun()
{
var d,i;
var t = document.getElementById("table");
var rows = t.getElementsByTagName("tr");
for (i = 0; i < rows.length; i++) {
console.log("inside............." + i);
rows[i].onclick = function() {
d = (this.rowIndex);
console.log(d);
};
}
}
Though I'm not sure it's what's causing the exact issue you're noticing, you've encountered a pretty common JavaScript pitfall here by using a closure (anonymous function) inside of a loop. JavaScript, like many other languages that support functional programming, has the convenient property that functions can "close scope" around any variables visible to them at the time of their creation. So, as you've done there, you can use the value of d (or i) inside your function so long as it can see them when your function is declared.
Something funny happens inside a loop, though: every function you create within the loop shares the same scope, meaning they all share the exact same copies of d and i. As a result, when you click on any of your rows, the values of d and i used will be their values at the end of the loop, not the particular iteration you're targeting.
This is ordinarily fixed using something known as the "generator pattern," where you create a separate function that returns new functions closed over your desired scope. For example, in your code, you might do something like
function generateClickHandler(i, d) {
return function() {
d = (this.rowIndex);
console.log(d);
};
}
function sun()
{
var d,i;
var t = document.getElementById("table");
var rows = t.getElementsByTagName("tr");
for (i = 0; i < rows.length; i++) {
console.log("inside............." + i);
rows[i].onclick = generateClickHandler(i, d);
}
}
The new function generateClickHandler returns a function itself, but the important thing to notice here is that the returned function closes over the local arguments i and d, not the shared i and d values used in the loop — their values get copied when you call generateClickHandler. In this way, your code won't be subject to strange closure effects.

Categories

Resources