I was for quite some time under the impression that a for loop could exist solely in the following format:
for (INITIALIZER; STOP CONDITION; INC(DEC)REMENTER)
{
CODE
}
This is, however, most definitely not the case; take a look at this JavaScript implementation of the Fisher-Yates Shuffle:
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;
};
This little snippet completely blows my mind; how in the world is so much going on inside a simple for loop declaration? I mean... it doesn't even open a brace! All of the magic is being done right there inside the for statement. It'd be absolutely wonderful if somebody could provide a relatively thorough explanation as to how in the world this voodoo is doing what it does. Much appreciated in advance.
shuffle = function(o){
for (
var j, // declare j
x, // declare x
i = o.length; // declare i and set to o.length
i; // loop while i evaluates true
j = parseInt(Math.random() * i), // j=random number up to i
x = o[--i], // decrement i, and look up this index of o
o[i] = o[j], // copy the jth value into the ith position
o[j] = x // complete the swap by putting the old o[i] into jth position
);
return o;
};
This is starting with i equal to the number of positions, and each time swapping the cards i and j, where j is some random number up to i each time, as per the algorithm.
It could be more simply written without the confusing comma-set, true.
By the way, this is not the only kind of for loop in javascript. There is also:
for(var key in arr) {
value = arr[key]);
}
But be careful because this will also loop through the properties of an object, including if you pass in an Array object.
The generalized format of a for loop (not a for-in loop) is
for ( EXPRESSION_1 ; EXPRESSION_2 ; EXPRESSION_3 ) STATEMENT
The first EXPRESSION_1 is usually used to initialize the loop variable, EXPRESSION_2 is the looping condition, and EXPRESSION_3 is usually an increment or decrement operation, but there are no rules that say they have to behave like that. It's equivalent to the following while loop:
EXPRESSION_1;
while (EXPRESSION_2) {
STATEMENT
EXPRESSION_3;
}
The commas are just an operator that combines two expressions into a single expression, whose value is the second sub-expression. They are used in the for loop because each part (separated by semicolons) needs to be a single expression, not multiple statements. There's really no reason (except maybe to save some space in the file) to write a for loop like that since this is equivalent:
shuffle = function(o) {
var j, x;
for (var i = o.length; i > 0; i--) {
j = parseInt(Math.random() * i);
x = o[i - 1];
o[i - 1] = o[j];
o[j] = x;
}
return o;
};
INITIALIZER can declare and initialize multiple variables. STOP CONDITION is a single test (here it's just "i"), and INCREMENTER is an expression to be executed each time after body (the comma operator lets you have multiple sub-expressions, which all get executed. ). The body of the for loop is just the empty statement ";"
The code you quote is obfuscated in my opinion. There are much clearer ways to write the same functionality.
However, your understanding is pretty much right. The following is the exact same code, except for whitespace and comments.
for (
// Initializer
var j, x, i = o.length;
// Continue condition
i;
// Operation to be carried out on each loop
j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x
)
// empty body, equivalent to { }
;
It's much clearer to write the equivalent:
var j,x,i = o.length;
while(i) {
j = parseInt(Math.random() * i);
x = o[--i];
o[i] = o[j];
o[j] = x;
}
There are other optimisations that could be made for readability - including using while(i > 0) instead of while(i), and splitting out the --i into an i-- on a separate line.
There's really no reason for for() to exist, except for readability. These two are equivalent:
{ // this block is to scope int i
int i=0;
while(i<100) {
myfunc(i);
i++;
}
}
for(int i=0; i<100; i++) {
myfunc(i);
}
You should use whichever is most readable for a given time. I'd argue that the author of your code has done the opposite. In fairness, he may have done this in order to achieve a smaller JS file for faster loading (this is the kind of transform an automated code compactor could do).
Syntax of for loop is:
for (pre-block; condition; post-loop-block)
loop-block;
First, pre-block is executed, various variables are defined.
In each loop:
check condition
execute loop-block
execute post-loop-block
repeat from 1.
That statement does comply with your initial format.
It turns out you could add more than one sentence of each using "," ( comma )
So:
for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
Could be analyzed like this:
for (var j, //INITIALIZER(s)
x,
i = o.length;
i; // STOP CONDITION ( i )
j = parseInt(Math.random() * i), // INC(DEC)REMENTER
x = o[--i],
o[i] = o[j],
o[j] = x); // CODE ( ; )
As you see, it fits completely in your initial format.
They've pretty much just moved the body of the loop into the incrementer section. You can re-write the for loop as a while loop to get some idea of what it is doing:
shuffle=function(o) {
var j; //Random position from 0 up to the current position - 1
var x; //temp holder for swapping positions
var i=o.length; //current position
while(i>0) { // Loop through the array
j = parseInt(Math.random()*i); //get a lower position
x = o[--i]; // decrement the position and store that position's value in the temp var
o[i]=o[j]; // copy position j to position i
o[j]=x; // copy the temp value that stored the old value at position i into position j
}
return o;
}
The first three var's are the initialzier expanded out, the check in the while is the stop condition and the body of the while is what was done in the incrementer portion of the for.
Edit: Corrected per Gumbo's comment
this goes all the way back to C syntax - from which javascript has stole a bunch. the main trick is the comma-operator which seems to appear in almost no other place except for loops
The first clause initializes any variables you want to use. The second clause is indeed the stop condition. The third clause includes any logic to be executed at the end of each iteration. Multiple statements can be separated by commas.
Related
I am new to javascript. Now, I want to make comparison of two website pair by iterating two string array as a pair the same time. These two string array's length are the same. I have searched for sites but didn't find the way to do in javascript. For example, in python, people can do this by using zip(), referencing from
How to merge lists into a list of tuples?.
However, in javascript, I try to something similar to that, but it will iterate over the second list every time it iterate over the element of first list, which is not want I wanted.
codes not what I expected
var FistList=['https://test1-1/travel','https://test1-1/cook','https://test1-1/eat'];
var SecondList=['https://test1-2/travel','https://test1-2/cook','https://test1-2/eat'];
FirstList.forEach(firstListItem => {
SecondList.forEach(secondListItem => {
//do comparison for these two websites....
});
});
What I expect is to do comparison pair by pair, which is =>
first loop: do comparison of 'https://test1-1/travel' and 'https://test1-2/travel'
second loop: do comparison of 'https://test1-1/cook' and 'https://test1-2/cook'
third loop: do comparison of 'https://test1-1/eat' and 'https://test1-2/eat'
I searched for a whole day but cannot find the way to do in javascript. Please advise. Thanks in advance!
If all you want is to compare values in same position of each array just use the index argument of forEach to reference array element in other array
var FirstList=['https://test1-1/travel','https://test1-1/cook','https://test1-1/eat'];
var SecondList=['https://test1-2/travel','https://test1-1/cook','https://test1-1/eat'];
FirstList.forEach((str, i) => console.log(str === SecondList[i]))
I think a very similar question was already answered here: How to compare arrays in JavaScript?
Accepted answer (https://stackoverflow.com/a/14853974/1842205) describes in depth how you could achieve such a goal.
JavaScript lacks of such a feature like mentioned zip() method from Python. But we have something like prototyping in JS :). And you can create 2D array like below:
function createArray(length) {
var arr = new Array(length || 0),
i = length;
if (arguments.length > 1) {
var args = Array.prototype.slice.call(arguments, 1);
while(i--) arr[length-1 - i] = createArray.apply(this, args);
}
return arr;
}
Array.prototype.zip = function (secondArr) {
let result = createArray(secondArr.length, 2);
for (let i = 0; i < this.length; i++) {
result[i][0] = this[i];
result[i][1] = secondArr[i];
}
return result;
};
// usage
var FistList=['https://test1-1/travel','https://test1-1/cook','https://test1-1/eat'];
var SecondList=['https://test1-2/travel','https://test1-2/cook','https://test1-2/eat'];
console.log(JSON.stringify(FistList.zip(SecondList)));
I like the OP's idea of making a more functional solution using zip, which can be home-rolled or reused from loadash or underscore.
const firstArray=['https://test1-1/travel','https://test1-1/cook','https://test1-1/eat'];
const secondArray=['https://test1-2/travel','https://test1-2/cook','https://test1-2/eat'];
const zipped = _.zip(firstArray, secondArray)
const compared = zipped.map(([first, second]) => first === second)
console.log(compared)
// reduce the pairwise comparison to a single bool with every()
// depends on requirements, but probably true iff every comparison is true
const arraysMatch = compared.every(e => e)
console.log(arraysMatch)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
Note that more functional solutions often involve the creation of some intermediate arrays (aka garbage) which is fine for small inputs.
I think the purpose of a forEach loop is to iterate over 1 list only. I would consider using a generic for loop to serve this purpose.
EDIT: I edited the code, and added a string prototype function to calculate the Levenstein distance between 2 strings. It's not rigid to detect for an edit in the exact spot your strings are changed in the examples. But I expect the examples are probably not totally reflective of your real data anyway, so instead of giving you some questionable regex, I'm giving you Levenstein and hope you understand it doesn't care where the difference is, it just cares how much has changed. In the example I only allow 1 character or less of difference: if (diff <= 1) {
//Define a string function for Levenstein Edit Distance
//call it "distancefrom" for clarity
String.prototype.distancefrom = function(string) {
var a = this, b = string + "", m = [], i, j, min = Math.min;
if (!(a && b)) return (b || a).length;
for (i = 0; i <= b.length; m[i] = [i++]);
for (j = 0; j <= a.length; m[0][j] = j++);
for (i = 1; i <= b.length; i++) {
for (j = 1; j <= a.length; j++) {
m[i][j] = b.charAt(i - 1) == a.charAt(j - 1)
? m[i - 1][j - 1]
: m[i][j] = min(
m[i - 1][j - 1] + 1,
min(m[i][j - 1] + 1, m[i - 1 ][j] + 1))
}
}
return m[b.length][a.length];
}
//Your Code
var FirstList=['https://test1-1/travel','https://test1-1/cook','https://test1-1/eat', 'https://waffles.domain/syrup', 'http://pancakes.webpresence/butter'];
var SecondList=['https://test1-2/travel','https://test1-2/cook','https://test1-2/eat', 'https://waffles.domain/syrups', 'https://pancakes.webpresence/buttery'];
for (let i=0; i < FirstList.length; i++) {
let diff = FirstList[i].distancefrom(SecondList[i]);
console.log('"'+FirstList[i]+'" is different than "'+SecondList[i]+'" by '+diff+' characters');
if (diff <= 1) {
console.log('Since its less than 1 character of difference, it would technically Pass our test.');
} else {
console.log('Since its more than 1 character of difference, it would Fail our test!');
}
console.log('-----------------');
}
References:
Levenstin Gist by scottgelin on GitHub
I have tried to implement this knapsack problem solution algorithm in JavaScript, but the solutions s_opt I get has a total weight greater than the L_max.
What am I doing wrong?
I suspect it could be something related to Closures in recursion.
/*
GENERAL:
Assume we have a knapsack and we want to bring as much stuff as possible.
Of each thing we have several variants to choose from. Each of these variants have
different value and takes different amount of space.
DEFINITIONS:
L_max = integer, size of the knapsack for the entire problem having N items
l = matrix, having the elements l[i-1][j-1] representing the space taken
by variant j of item i (-1 since indexing the matrices has index starting on zero, i.e. item i is stored at position i-1)
p = matrix, having the elements p[i-1][j-1] representing the value given by
by variant j of item i
n = total number of items (used in a sub-problem)
N = total number of items (used in the full problem, N >= n)
s_opt = vector having the optimal combination of variant selections s_i, i.e. s_opt = arg max p_sum
*/
function knapsack(L_max,l,p) {
// constructing (initializing) - they are private members
var self = this; // in order for private functions to be able read variables
this.N = l.length;
var DCached = []; // this is only used by a private function so it doesnt need to made public using this.*
this.s_opt = [];
this.p_mean = null;
this.L_max = L_max;
// define public optimization function for the entire problem
// when this is completed the user can read
// s_opt to get the solution and
// p_mean to know the quality of the solution
this.optimize = function() {
self.p_mean = D(self.N,self.L_max) / Math.max(1,self.N);
}
// define private sub-problem optimization function
var D = function(n,r) {
if (r<0)
return -Infinity;
if (n==0)
return 0;
if(DCached[n-1] != null) {
if(DCached[n-1][r-1] != null) {
return DCached[n-1][r-1];
}
}
var p_max = -Infinity;
var p_sum;
var J = l[n-1].length;
for(var j = 0; j < J; j++) {
p_sum = p[n-1][j] + D( n-1 , r - l[n-1][j] );
if(p_sum>p_max) {
p_max = p_sum;
self.s_opt[n-1] = j;
}
}
DCached[n-1] = [];
DCached[n-1][r-1] = p_max;
return p_max;
}
}
The client using this knapsack solver does the following:
var knapsackSolution = new knapsack(5,l,p);
knapsackSolution.optimize();
// now the client can access knapsackSolution.s_opt containing the solution.
I found a solution. When solving a sub-problem D(n,r) the code in the question returned the optimized value, but it didn't really manage the array s_opt in a proper way. In the modified solution, pasted below, I fixed this. Instead of only returning the optimized value of the knapsack also an array of chosen variants (e.g. the arg of the max) are returned. The cache is also modified to manage these two parts of the solution (both max value and arg max value).
The code below also contains an additional feature addition. The user can now also pass a value maxComputingComplexity controlling the computational size of the problem in some kind of heuristic manner.
/*
GENERAL:
Assume we have a knapsack and we want to bring as much stuff as possible.
Of each thing we have several variants to choose from. Each of these variants have
different value and takes different amount of space.
The quantity of each variant is one.
DEFINITIONS:
L_max = integer, size of the knapsack, e.g. max number of letters, for the entire problem having N items
l = matrix, having the elements l[i-1][j-1] representing the space taken
by variant j of item i (-1 since indexing the matrices has index starting on zero, i.e. item i is stored at position i-1)
p = matrix, having the elements p[i-1][j-1] representing the value given by
by variant j of item i
maxComputingComplexity = value limiting the product L_max*self.N*M_max in order to make the optimization
complete in limited amount of time. It has a serious implication, since it may cut the list of alternatives
so that only the first alternatives are used in the computation, meaning that the input should be well
ordered
n = total number of items (used in a sub-problem)
N = total number of items (used in the full problem, N >= n)
M_i = number of variants of item i
s_i = which variant is chosen to pack of item i
s = vector of elements s_i representing a possible solution
r = maximum total space in the knapsack, i.e. sum(l[i][s_i]) <= r
p_sum = sum of the values of the selected variants, i.e. sum(p[i][s_i]
s_opt = vector having the optimal combination of variant selections s_i, i.e. s_opt = arg max p_sum
In order to solve this, let us see p_sum as a function
D(n,r) = p_sum (just seeing it as a function of the sub-problem n combined with the maximum total space r)
RESULT:
*/
function knapsack(L_max,l,p,maxComputingComplexity) {
// constructing (initializing) - they are private members
var self = this; // in order for private functions to be able read variables
this.N = l.length;
var DCached = []; // this is only used by a private function so it doesnt need to made public using this.*
//this.s_opt = [];
//this.p_mean = null;
this.L_max = L_max;
this.maxComputingComplexity = maxComputingComplexity;
//console.log("knapsack: Creating knapsack. N=" + N + ". L_max=" + L_max + ".");
// object to store the solution (both big problem and sub-problems)
function result(p_max,s_opt) {
this.p_max = p_max; //max value
this.s_opt = s_opt; //arg max value
}
// define public optimization function for the entire problem
// when this is completed the user can read
// s_opt to get the solution and
// p_mean to know the quality of the solution
// computing complexity O(L_max*self.N*M_max),
// think O=L_max*N*M_max => M_max=O/L_max/N => 3=x/140/20 => x=3*140*20 => x=8400
this.optimize = function() {
var M_max = Math.max(maxComputingComplexity / (L_max*self.N),2); //totally useless if not at least two
console.log("optimize: Setting M_max =" + M_max);
return D(self.N,self.L_max,M_max);
//self.p_mean = mainResult.D / Math.max(1,self.N);
// console.log...
}
// Define private sub-problem optimization function.
// The function reads to "global" variables, p and l
// and as arguments it takes
// n delimiting the which sub-set of items to be able to include (from p and l)
// r setting the max space that this sub-set of items may take
// Based on these arguments the function optimizes D
// and returns
// D the max value that can be obtained by combining the things
// s_opt the selection (array of length n) of things optimizing D
var D = function(n,r,M_max) {
// Start by checking whether the value is already cached...
if(DCached[n-1] != null) {
if(DCached[n-1][r-1] != null) {
//console.log("knapsack.D: n=" + n + " r=" + r + " returning from cache.");
return DCached[n-1][r-1];
}
}
var D_result = new result(-Infinity, []); // here we will manage the result
//D_result.s_opt[n-1] = 0; // just put something there to start with
if (r<0) {
//D_result.p_max = -Infinity;
return D_result;
}
if (n==0) {
D_result.p_max = 0;
return D_result;
}
var p_sum;
//self.s_opt[n] = 0; not needed
var J = Math.min(l[n-1].length,M_max);
var D_minusOneResult; //storing the result when optimizing all previous items given a max length
for(var j = 0; j < J; j++) {
D_minusOneResult = D( n-1 , r - l[n-1][j] , M_max)
p_sum = p[n-1][j] + D_minusOneResult.p_max;
if(p_sum > D_result.p_max) {
D_result.p_max = p_sum;
D_result.s_opt = D_minusOneResult.s_opt;
D_result.s_opt[n-1] = j;
}
}
DCached[n-1] = [];
DCached[n-1][r-1] = D_result;
//console.log("knapsack.D: n=" + n + " r=" + r + " p_max= "+ p_max);
return D_result;
}
}
I try to make code like this:
var code1 = a, code2 = b, code3 = c;
var x = 3;
for (y = 1; y <= x; y++) {
//this part where i dont know about
alert ();
}
So how to make it alert code1, code2, and code3? I mean this alerts the values a, b, and c.
I tried with alert("code"+y); and alert(code+y); but it wont do.
So how to make it alert code1, code2, and code3? i mean this alert the value a, b, and c?
The best way is to use an array instead of discrete code1, code2, and code3 variables:
// (I assume a, b, and c have already been declared somewhere, or that your real
// code has literals?)
var codes = [a, b, c];
var y;
for (y = 0; y < codes.length; y++) {
alert(codes[y]);
}
(Note that I started y in a different place.)
While it's possible to do the code1, code2, code3 thing with global variables, global variables should be avoided whenever possible, and it's nearly always possible. (It's also possible with local variables, but you have to use eval or its cousin the Function constructor, and avoiding eval is also something you should avoid whenever possible, and is nearly always possible. :-) )
Alternately, if you find yourself wanting to do this where an array doesn't quite make sense, you can use an object instead:
var codes = {
code1: a,
code2: b,
code3: c
};
var y;
for (y = 1; y <= 3; ++y) {
alert(codes["code" + y]);
}
That works because in JavaScript, you can access an object property using either dot notation and a literal (obj.foo), or brackets notation and a string (obj["foo"]), and in the latter case the string can be the result of any expression. Since "code" + y is code1 when y is 1, codes["code" + y] looks up the property "code1" on codes (when y is 1).
Use Bracket notation
alert(window["code"+y]);
I would rather recommend you to use an array like
var code = [1, 2, 3];
for (y = 0; y < code.length; y++) {
alert(code[y]);
}
I have two arrays set up that I wish to multiply each value within each together. Then I want to get the total value in the form of a variable. I will post what I have below. I think my problem may be that I am not sure how to get each run of the code to add together?
var flatQty=[];
flatQty[0]= document.getElementById("flats1").value;
flatQty[1]= document.getElementById("flats2").value;
flatQty[2]= document.getElementById("flats3").value;
flatQty[3]= document.getElementById("flats4").value;
flatQty[4]= document.getElementById("flats5").value;
var flatWidth=[];
flatWidth[0]=document.getElementById("flatwidth1").value;
flatWidth[1]=document.getElementById("flatwidth2").value;
flatWidth[2]=document.getElementById("flatwidth3").value;
flatWidth[3]=document.getElementById("flatwidth4").value;
flatWidth[4]=document.getElementById("flatwidth5").value;
for (var i=0;i<5;i++)
{
var flatCharge=flatWidth[i]*2*flatQty[i];
}
document.getElementById("flatTest").innerHTML=flatCharge;
When I run the code nothing is printed into the id="flatTest".
Your problems is that you are redefining your flatCharge inside the loop, therefore it's not correct outside the loop. In addition, you are not adding the values, but replacing them on every iteration of the loop. Change the loop to this:
var flatCharge = 0;
for (var i = 0; i < 5; i++) {
flatCharge += flatWidth[i] * 2 * flatQty[i];
};
document.getElementById("flatTest").innerHTML = "" + flatCharge;
and it should work.
.value properties are strings, not numbers. so you should be careful how you handle them. Multiplication actually works for strings, but not for addition where the + operator performs concatenation instead.
There are numerous methods of converting from string to number:
+s - will convert the expression s into a number
parseFloat(s)
parseInt(s, 10) for whole numbers
The actual problem in your code is that you're overwriting the calculated value in each pass using the = operator instead of +=.
I suggest refactoring your entire code thus to avoid all of the repetition:
var flatCharge = 0;
for (var i = 1; i <= 5; ++i) {
var qty = +document.getElementById('flats' + i).value;
var width = +document.getElementById('flatwidth' + i).value;
if (!isNaN(qty) && !isNaN(width)) {
flatCharge += 2 * qty * width;
}
}
for (var i=0;i<5;++i){
alert(i);
}
for (var i=0;i<5;i++){
alert(i);
}
These two constructs return the same result: 0,1,2,3,4. Why? What are the differences between them? Does it matter what increment i use in for loop?
If you put ++ in front of the variable you increment the value before returning it (in that statement), if you put it behind you return the value, then increment it afterwards. Since you are doing nothing with the value in the statement the result after said statement is the same.
Consider this:
var i = 0;
var a = ++i; // a is 1
var b = i++; // b is also 1, i is now 2.
The former is a pre-increment, the latter a post-increment.
The difference is nothing your example as you're not assigning the result to anything, but show themselves quite alot when assigning the result to another variable.
var i = 0;
alert(i); // alerts "0"
var j = i++;
alert(j); // alerts "0" but i = 1
var k = ++i;
alert(k); // alerts "2" and i = 2
Live example: http://jsfiddle.net/ggUGX/
for a loop you dont see any difference, but the ++i increments and then returns the value wheras i++ returns a value and then increments. If you have code like
var a = myarray[++i]
and
var a = mayarray[i++];
they will return differnet values
These two code blocks should have the same output. The difference between i++ and ++i is the order in which the variable i is incremented and is only important when using the value of i at the same time.
For instance, ++i and i++ do effectively the same thing unless you're using it like so:
y = i++;
or
y = ++i;
In the first example i is incremented AFTER y is set to its value (so if i = 0, y = 0, then i = 1). In the second example i is incremented BEFORE y is set to its value (so if i = 0, i = 1, y = 1).
Because you do not use i++ in a similar fashion in a for statement, it has no effective difference.
i++ or ++i in the for loop executes as a different statements. So, putting i++ or ++i in for loop doesn't make any difference.