JavaScript - Factorialization 'For Loop' Understanding? - javascript

I am in the midst of completing some JavaScript algorithmic challenges and I had to factorialize a number as part of one of them. After searching through stack and other places I entered a correct code block:
function factorialize(num) {
if(num === 0) {
return 1;
}
if(num < 0 ) {
return undefined;
}
for(var i = num; --i; ) {
num *= i;
}
return num;
}
factorialize(5);
It it returns a correct result. What I am struggling to understand however is why the for loop doesn't have a second statement, and why it can run for ever? I have an inkling it's because as soon as i value is 0, any subsequent negative number that is generated will be multiplied by 0 and so only the integer numbers will form the result. But why does the function return a valid number, if the loop is still running to -infinity and hasn't been told to stop when reaching a certain value?

the second part of your for loop is the Condition:
An expression to be evaluated before each loop iteration. If this expression evaluates to true, statement is executed. This conditional test is optional. If omitted, the condition always evaluates to true. If the expression evaluates to false, execution skips to the first expression following the for construct.
Once --i reaches 0, it evaluates to false (falsey) and the for "exits"
adding a console.log(i) to your for loop will help demonstrate that
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for

All elements in a for-loop's expression are optional.
The second part of the for-loop is used to express the for-loop's condition. The condition is evaluated on every iteration, and when it evaluates to false, the loop is exited.
In this case, that second part that expresses the condition is --i. This means that on every iteration, i will be decremented by 1, until it finally reaches zero (0). Since 0 is considered to be a falsey value in Javascript, the loop exits.

for (a; b; c)
is a direct equivalent to
a;
while (b) {
c;
}
You're mistaken here because you seem to think that c is what determines the end of the loop, but b actually is. You can omit the third loop statement if you don't need it (as well as the first one).
Here, your loop is equivalent to:
var i = num;
while (--i) {
num *= i;
}
while (--i) does two things:
Decrement i (before anything else)
Check that i != 0 (this is the actual loop condition).
If it was i--, note that the operations would be done in the opposite order.
If you find the code hard to read, you can also write it this way for the same results:
for (var i = num - 1; i != 0; i--) {
num *= i;
}

Normally you would have :
for(var i = 0; i < num; i++)
the second statement is a boolean continuation expression.
So when you have
for(var i = num; i--;)
The second is still a boolean continuation expression and when i get to 0 it evaluated to false.

Related

Javascript/jQuery loop number with half increment [duplicate]

This question already has answers here:
Can a for loop increment/decrement by more than one?
(9 answers)
Closed 1 year ago.
I need to create loop number but with increment 0.5
for(var i = 1; i <= 10; i < i++){
var newOption = $('<option value="'+i+'">'+i+' Minute</option>');
$('.duration').append(newOption);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select class="duration"></select>
What I expected is like this:
1
1.5
2
2.5
3
3.5
So on until 10
Any idea how to do the trick?
Just use i+= 0.5 like this:
for(var i = 1; i <= 10; i+= 0.5){
var newOption = $('<option value="'+i+'">'+i+' Minute</option>');
$('.duration').append(newOption);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select class="duration"></select>
Just use i+= 0.5 in the last part of loop.
for(var i = 1; i <= 10; i+= 0.5) {
console.log(i)
}
A for loop has 3 parts:
for ([initialExpression]; [conditionExpression]; [incrementExpression])
statement
When a for loop executes, the following occurs:
The initializing expression initialExpression, if any, is executed. This expression usually initializes one or more loop counters, but the syntax allows an expression of any degree of complexity. This expression can also declare variables.
The conditionExpression expression is evaluated. If the value of conditionExpression is true, the loop statements execute. If the value of condition is false, the for loop terminates. (If the condition expression is omitted entirely, the condition is assumed to be true.)
The statement executes. To execute multiple statements, use a block statement ({ ... }) to group those statements.
If present, the update expression incrementExpression is executed.
Control returns to Step 2.
So you can set any incrementExpression you want, like += 0.5 or anything else.
Loops and iteration

Why doesn't the for loop stop on les then operator (<) but continues to loop one more time to be (=) to 5

The operator is les then or equal. So why does it continue to loop one more time since when it looped 4 times, it reached one of it's conditions?
for (let i = 0; i <= 5; i++) {
console.log(i);
console.log("Hello World!");
}
You are correct that <= means less than or equal, but I believe you are confusing how loops work. The second statement in a for-loop declaration is called the condition, and defines when a loop can continue to execute. If the condition returns true, the loop body is executed. If false, it breaks.
If it helps, you can imagine it as a while loop where
for(declaration; condition; increment)
{
//loop body
}
is fundamentally equivalent to
declaration;
while(condition)
{
//loop body
increment;
}
A condition of i <= 5 means the loop will continue to execute as long as the i is less than or equal to 5. It does not mean the loop will break if i is no longer both less than nor equal to 5.
The simple change is to use i < 5.
The i <= 5 condition has to evaluate to false for the control to break the loop. And that happens when the value of i is 6.
because you start with zero or use <5 instead of <= if i=0
for (let i = 1; i <= 5; i++) {
console.log(i);
console.log("Hello World!");
}

Javascript exercise that looks fine to me but its not working

The exercise is about identifying if all elements in an array are the same and return true if they are or false if they aren't. Below is the code & my logic behind writing the code.
function isUniform(array){
for(var i = array.length - 1; i>=0; i--){
if(array[i] !== array[i-1]){
return false;
}
}
return true;
}
Basically I want to start from the end of the array with the last element and check if its equal with the second-to-last element.If they're equal, the loop will subtract 1 from the "i" variable and the "if statement" will run again. The loop will stop when i reaches -1 and thats the point where every array element was checked and the loop should end, returning true. What am I doing / thinking wrong?
Thanks!
When i becomes 0, you are comparing arr[0] with arr[-1] which is wrong. Your checking condition should be i > 0.
The very last time it run, i is 0, so you're comparing array[0] with array[-1] which is incorrect. Your Boolean condition should be i > 0 so you avoid this issue:
function isUniform(array){
for(var i = array.length - 1; i > 0; i--){
if(array[i] !== array[i-1]){
return false;
}
}
return true;
}
You can use every method for a simplified solution.
const allEqual = arr => arr.every(x => arr[0] == x));
You could create a method that checks the array for your input using ArrayUtils.
public boolean contains(final int[] array, final int key) {
return ArrayUtils.contains(array, key);
}
Traveling so can't debug, but the last iteration of i will be 0 in your code and stop.

having trouble understanding this recursion

I need to find the sum of integers in an array using recursion.
The following code throws an error :
var sum = function(array) {
if(array.length === 0) return 0;
while(array.length) {
sum = array[0] + sum(array.slice(1));
return sum;
}
}
while this works :
var sum = function(array) {
if(array.length === 0) return 0;
while(array.length) {
return array[0] + sum(array.slice(1));
}
}
The difference is the way the sum is returned. Can someone explain?
As mentioned in comments, the while loop never loops - it executes a return statement before testing the loop condition a second time.
Even so, why does the first version work the first time it is called but not the second?
sum = array[0] + sum(array.slice(1));
stores the right hand expression value in sum, after the function call to sum returns. That variable is in scope, so the assignment is valid.
But the assignment doesn't occur until after the call to sum (on the right hand side) returns. Each time sum returns, assignment overwrites the variable sum with the partial, and eventually the final result.
So the first call works successfully, but leaves sum set to the result of the first call. Since you can't call a number, it errors the second time you call it.

Select2 each2 method - how does it work?

I was looking through Select2 (source code) and found each2 method prototype:
$.extend($.fn, {
each2 : function (c) {
var j = $([0]), i = -1, l = this.length;
while (
++i < l
&& (j.context = j[0] = this[i])
&& c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
);
return this;
}
});
My question is - how this method works? I mean - why there is while loop only with condition, without statement part? I'd really love to understand this method's flow.
When you put an expression inside a condition (for instance: if (i), if (i == null), if (++i), if (i < 2))) the expression get evaluated before its 'checked' is it true or false.
Live example:
we have var i = 0; now you call if (++i) console.log(i). The expression ++i return 1 (it is understood as truth in javascript [0 is not]) so console.log(i) logs 1.
Now let's say we have var i = 0 and if (++i && ++i) console.log(i). The first expressions returns 1 so the second is also called which returns 2. Both 1 and 2 are treated as truth so console.log(i) logs 2
Awesome. Let's have a look on the same example as above but before if we initialise var i = -1
. Now we call if (++i && ++i) console.log(i). The first ++i returns 0 which is falsity.
To continue you have to get how && works. Let me explain quickly: If you stack exp1 && exp2 && exp3 ... && expX then exp2 will be only executed(evaluated) when exp1 returns truth (for instance: true, 1, "some string"). exp3 will be executed only if exp2 is truthy, exp4 when exp3 is truth and so on...(until we hit expN)
Let's go back to f (++i && ++i) console.log(i) now. So the first ++i returns 0 which is falsity so second ++i isn't executed and whole condition is false so console.log(i) won't be executed (How ever incrementation was completed so i equals to 0 now).
Now when you get it I can tell you that while loop works the same in condition checking way. For instance var = -2 and while (++i) will be executed until ++i returns falsity (that is 0). while (++1) will be executed exactly 2 times.
TL;DR BELOW!!!
So how this works?
while (
++i < l
&& (j.context = j[0] = this[i])
&& c.call(j[0], i, j) !== false
);
I think the best way to explain is to rewrite it (:
while ( ++i < l ) {
if (!(j.context = j[0] = this[i])) break;
if (!(c.call(j[0], i, j) !== false)) break;
}
or ever less magical:
var i = 0;
while ( i < l ) {
if (!(j.context = j[0] = this[i])) break;
if (!(c.call(j[0], i, j) !== false)) break;
i++;
}
The purpose of the code is to add a new function each2 to the jQuery object that works similar to .each. The usage of this .each2 function is same as .each which executes over a jQuery object, takes a function to call back with 2 arg (index and value) and returns jQuery object.
The mysterious while loop,
while (
++i < l
&& (j.context = j[0] = this[i])
&& c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
);
return this;
}
Observations:
Continues "only if" all 3 conditions evaluates to true
First Condition: ++i < l, a simple iterate evaluates to true until the value of i is less than count of selected elements. [i is initialized to -1, because of the usage of ++i in next line and i is used as index in later references]
Second Condition: It is actually an initialization + null/undefined check. The condition is evaluated to true for all valid values and fails when there is an invalid context.
Third condition: The purpose of this condition allows you to break out of the loop if the call back function return false. Same as .each where return false inside the function will break the iteration.
As noted by OP, there is no statement after the while condition. Note the semicolon after the while loop, return this is only run after the while loop has been completed, and that is when any one of the three statements evaluate to false. If all you want to know is how the while loop is working, Filip Bartuzi's answer is probably the most complete. I will attempt to give a broader answer as to what the each2 method does.
As noted in the docs, it is just a more efficient version of jQuery's #each, or Array.prototype.forEach or Underscore's #each or lodash's #forEach that is designed specifically for select2's uses. It is used to iterate over any array or other iterable, wrapped in a jQuery object. So it is used for arrays of strings as well as jQuery collections.
The way it works is the scope (this) is the array that it was called on. It is given a function as an argument. This is exactly how the other each methods I previously mentioned are called. The function provided to each2 is called once for each item in the array. The while loop is sort of a hacky way to iterate over each item in the array, and call the function, setting the item as the context and passing the index in the array and the item as a jQuery object as the first and second arguments. Each statement in the while loop condition must be evaluated to determine if it is true, and that process is being used to actually assign values to variables, and increment i. The c.call(j[0], i, j) !== false part allows the function to terminate the loop early by returning false. If the function returns false, the while loop will stop, otherwise it will continue until i is greater than l, meaning that every item in the array has been used. Returning this afterwards just enables another method to be chained to the array after .each2.
Basically the while loop could be rewritten to:
var j = $([0]), i = 0, l = this.length, continue = true;
while (i < l) {
i++;
j.context = j[0] = this[i];
continue = c.call(j[0], i, j);
if (!continue) {
break;
}
}
but there are probably some performance reasons why that can't be optimized as well by the browser.
It is more fragile than the normal each methods. For example if an element in the array has a falsey value such as 0, (j.context = j[0] = this[i]) will be falsey, causing the loop to terminate. But it is only used in the specialized cases of the select2 code, where that shouldn't happen.
Let's walk through a couple of examples.
function syncCssClasses(dest, src, adapter) {
var classes, replacements = [], adapted;
classes = $.trim(dest.attr("class"));
if (classes) {
classes = '' + classes; // for IE which returns object
$(classes.split(/\s+/)).each2(function() {
if (this.indexOf("select2-") === 0) {
replacements.push(this);
}
});
}
...
^ This code is getting the classes from a dest DOM element. The classes are split into an array of strings (the string is being split on whitespace characters, that's the \s+ regular expression), each class name is an item in the array. No special jQuery work is needed here, so the 2 arguments that the function called by each2 is provided are not used. If the class starts with 'select2-' the class is added into a array called replacements. The 'select2-' classes are being copied, pretty simple.
group.children=[];
$(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); });
^ For brevity I have not included the rest of this method, but it is part of the process method. In this case, each2 is being used to recursively process a node and all it's children. $(datum.children) is a jQuery selection, each 'child' (named childDatum) will be processed in turn, and it's children will go through the same process. The group.children array will be used as a collection, whatever is added to that array for each childDatum will be available, so after the each2 is run it will hold everything that has been added during the processing of all the children, grandchildren, etc.

Categories