JavaScript: simulating 'break' inside 'foreach' loop - javascript

What is the best way to implement a siulation of 'break' feature of a for-loop when you are iterating through an user/engine-defined function?
foreach([0,1,2,3,4],function(n){
console.log(n);
if (n==2)
break;});
I've thought in implementing foreach in a way that would break when the function returned 'false' - but I would like to hear thoughts on how that is normally done.

returning false is the most common way to do it. That's what jQuery's iterator function .each() does:
We can break the $.each() loop at a particular iteration by making the
callback function return false. Returning non-false is the same as a
continue statement in a for loop; it will skip immediately to the next
iteration.
And its very simplified implementation:
each: function( object, callback ) {
var i = 0, length = object.length,
for ( var value = object[0];
i < length && callback.call( value, i, value ) !== false; // break if false is returned by the callback
value = object[++i] ) {}
return object;
}

Related

If statement not working inside foreach loop javascript

I am trying to return the first value which is greater than 25 from an array, without using other methods such as filter. Assume the array is always sorted.
The function always returns undefined. Why is that happening? Here is the code:
function checkArr(arr){
arr.forEach(i => {
if (i>25) return i;
})
}
console.log(checkArr([10,20,34,45]))
The output should be 34.
When you use forEach, you execute another function for every item in the array. That function can return a value just fine, but it'll get completely ignored. If you want to return a value in the checkArr function, you have to do something like this:
function checkArr(arr) {
for (let i of arr) {
if (i>25) return i;
}
}
You can use find function instead of forEach. And you must return a result of this function to get an answer instead of undefined.
function checkArr(arr) {
return arr.find(i => i > 25);
}
console.log(checkArr([10,20,34,45]))
forEach function returns nothing, it just makes some actions for each element in an array.
Your function is not returning anything. And returning inside a forEach won't have any effect, since forEach always returns undefined. You can use a simple while loop to do this:
function checkArr(arr){
var i = 0;
while (i<arr.length) {
if (arr[i]>25) return arr[i];
i++;
}
return null; // if no entry is greater than 25
}
console.log(checkArr([10,20,34,45]));
The forEach() method executes a provided function once for each array element.
From the above statement it is clear that you can not return from the middle of the execution of the loop.
Use for...of loop instead to return as soon as the condition is true:
function checkArr(arr){
for(var item of arr){
if (item > 25) return item;
}
}
console.log(checkArr([10,20,34,45]))
Seems like you're looking for find:
function checkArr(arr) {
return arr.find(i => i>25);
}
console.log(checkArr([10,20,34,45]));
It's because you're passing a delegate function when calling .forEach.
The return of the delegate is getting lost and isn't applying to anything. To get your desired result, you'll want to exit the calling function checkArr.
This can be done using a simple for loop.
function checkArr(arr){
for (var i = 0; i < arr.length++; i++) {
if (arr[i] > 25) return arr[i];
}
}
console.log(checkArr([10,20,34,45]))
This approach also supports older browsers, unlike some, every and forEach
"Good code is short, simple, and symmetrical - the challenge is figuring out how to get there".
function checkArr(arr){
return arr.filter(m => m > 25)
}
checkArr([10,20,34,45]);

Confused about the `return` statement in javascript. Explanation needed

I posted a question not too long ago this morning regarding a kata that I was trying to solve. In that question, (found here if interested Kata Question) I needed to add a return statement to my function so that I would avoid the following error Value is not what was expected.
Now I have my second iteration of my kata solution to try out and here it is:
function isMerge(s, part1, part2) {
var pointer = 0
splitString = s.split('');
splitString.forEach(function(character) {
if (part1.includes(character) || part2.includes(character)) {
pointer++;
return true;
} else {
return false;
}
});
}
isMerge('codewars','cdw','oears')
I am still getting Value is not what was expected errors when I try to execute the code and this time I'm confused as to why in particular this happens.
For starters, taken from the MDN guide
The return statement ends function execution and specifies a value to be returned to the function caller.
expression
The expression to return. If omitted, undefined is returned instead.
Look at my if/else logic I am specifying a return true and return false condition in my forEach loop to see if all the chars from part1 and part2 are in the string. I am returning something so why is it that I have a Value is not what was expected?.
Second of all, by definition of the return statement, the function is supposed to stop when it reaches that keyword. However, when I place a console.log(character) in the logic, I can see on my console that all of the characters are being outputted so the function is not breaking at all when return true is executed. Why is that?
Third, I am confused as to when to use the return keyword in general. Consider these examples from the MDN docs for ForEach.
Example 1:
function logArrayElements(element, index, array) {
console.log('a[' + index + '] = ' + element);
}
// Notice that index 2 is skipped since there is no item at
// that position in the array.
[2, 5, , 9].forEach(logArrayElements);
// logs:
// a[0] = 2
// a[1] = 5
// a[3] = 9
Example 2:
function Counter() {
this.sum = 0;
this.count = 0;
}
Counter.prototype.add = function(array) {
array.forEach(function(entry) {
this.sum += entry;
++this.count;
}, this);
// ^---- Note
};
var obj = new Counter();
obj.add([2, 5, 9]);
obj.count
// 3
obj.sum
// 16
Not a single return statement to in these examples.
Now look at this .every example.
function isBigEnough(element, index, array) {
return element >= 10;
}
[12, 5, 8, 130, 44].every(isBigEnough);
And finally, from my previous question, I need to add a second return statement like this to avoid the value error.
function isBigEnough(element, index, array) {
return element >= 10;
}
function whenToUseReturn(array) {
return array.every(isBigEnough);
}
whenToUseReturn([12, 5, 8, 130, 44]);
So....... in conclusion, for my original function that started this how am I supposed to exit the loop when I reach false and return it and likewise when all the characters are in the string, how do I return a 'cumulative' true and avoid a Value error. I hope this makes sense and I can clarify with edits to better illustrate my point.
I am returning something so why is it that I have a Value is not what was expected?.
The return statement returns from the callback you pass to forEach, not from isMerge. return statements don't cross function boundaries. isMerge doesn't contain a return statement, hence it returns undefined. If we rewrite the function slightly it might become clearer:
function doSomething(part1, part2) {
return function(character) {
if (part1.includes(character) || part2.includes(character)) {
return true;
} else {
return false;
}
}
}
function isMerge(s, part1, part2) {
splitString = s.split('');
splitString.forEach(doSomething(part1, part2));
}
isMerge('codewars','cdw','oears')
This is equivalent to your code. As you can see, there is no return statement in isMerge.
Not a single return statement to in these examples.
There are no return statements in the forEach examples because forEach doesn't do anything with the return value of the callback, so there is no point in returning anything.
forEach is just a different way to iterate over an array, but it doesn't produce a value like reduce or every.
how am I supposed to exit the loop when I reach false and return it and likewise when all the characters are in the string, how do I return a 'cumulative' true and avoid a Value error.
You cannot exit a forEach "loop". If you have to stop the iteration early, you need to use a normal for (for/in, for/of) loop.
To return and produce a value, you can use your original solution that uses every.
My friend, since you decided to go the "callback way" using .each and the like, you should consider using callbacks, since you cannot return anything in this case. If you do not wish to go the callback way, just use standard javascript, such as:
splitString.forEach(function(character) {
Replace with
for(var i = 0 ; i < splitString.length; i++){
And now you can return. Using "each" to loop an array is just plain unnecessary and prevents you to return.

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.

How to return from a $.each operation? (jQuery)

Example:
var t = $.each(templos_cache, function(f,g){
$.each(g.requer, function(h,j){
if (parseFloat(g.id) == id){
alert(j.nome); // OK, return 'afrodite'.
return j.nome; // wrong, return [Object object].
}
});
return '';
});
Looking at the code we can see the problem... i can do a set in a variable out of scope but i think that can exist some more elegant way to do this.
To break out of $.each(), simply return false; from the callback function. As quoted from the jQuery documentation:
We can break the $.each() loop at a
particular iteration by making the
callback function return false.
Returning non-false is the same as a
continue statement in a for loop; it
will skip immediately to the next
iteration.
Edit:
Upon realising you may want to return a value, you could simply pass an object to the callback function as this is passed by reference. See How to append a string value during assignment in Javascript? - this isn't at all any more elegant though, so I'd stick with just setting a variable outside of $.each as colinmarc said.
Off the top of my head, setting a variable seems like the most elegant way to do it, like you mentioned:
var foo = '';
$.each(some_list, function(i, e){
...
if(something) foo = 'stuff';
});
in THEORY something like this should do what you want. it should loop through templos_cache, and inside loop through g.requer until id matches g.id. in that case it will set returnValue, and break out of the inside $.each loop. and in the outside loop it checks if returnValue has been set, if so, breaks out of the loop.
I haven't actually tested this. but it seems solid
var returnValue = '';
$.each(templos_cache, function(f,g){
$.each(g.requer, function(h,j){
if (parseFloat(g.id) == id){
returnValue = j.nome;
return false;
}
});
if(returnValue != ''){
return false;
}
});
var t = returnValue;
You may be looking for $.map instead:
var x = [1, 2, 3, 4, 5];
var y = $(x).map(function(i, n){
return n < 4 ? n+1 : undefined;
});
// y == [2, 3, 4]
If you only return one value, y will be [val] and you can always access it with y[0]

How to skip to next iteration in jQuery.each() util?

I'm trying to iterate through an array of elements. jQuery's documentation says:
jquery.Each() documentation
Returning non-false is the same as a continue statement in a for loop, it will skip immediately to the next iteration.
I've tried calling 'return non-false;' and 'non-false;' (sans return) neither of which skip to the next iteration. Instead, they break the loop. What am i missing?
What they mean by non-false is:
return true;
So this code:
var arr = ["one", "two", "three", "four", "five"];
$.each(arr, function(i) {
if (arr[i] == 'three') {
return true;
}
console.log(arr[i]);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
will log one, two, four, five.
By 'return non-false', they mean to return any value which would not work out to boolean false. So you could return true, 1, 'non-false', or whatever else you can think up.
Javascript sort of has the idea of 'truthiness' and 'falsiness'. If a variable has a value then, generally 9as you will see) it has 'truthiness' - null, or no value tends to 'falsiness'. The snippets below might help:
var temp1;
if ( temp1 )... // false
var temp2 = true;
if ( temp2 )... // true
var temp3 = "";
if ( temp3 ).... // false
var temp4 = "hello world";
if ( temp4 )... // true
Hopefully that helps?
Also, its worth checking out these videos from Douglas Crockford
update: thanks #cphpython for spotting the broken links - I've updated to point at working versions now
The Javascript language
Javascript - The Good Parts
Dont forget that you can sometimes just fall off the end of the block to get to the next iteration:
$(".row").each( function() {
if ( ! leaveTheLoop ) {
... do stuff here ...
}
});
Rather than actually returning like this:
$(".row").each( function() {
if ( leaveTheLoop )
return; //go to next iteration in .each()
... do stuff here ...
});
The loop only breaks if you return literally false. Ex:
// this is how jquery calls your function
// notice hard comparison (===) against false
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
break;
}
This means you can return anything else, including undefined, which is what you return if you return nothing, so you can simply use an empty return statement:
$.each(collection, function (index, item) {
if (!someTestCondition)
return; // go to next iteration
// otherwise do something
});
It's possible this might vary by version; this is applicable for jquery 1.12.4. But really, when you exit out the bottom of the function, you are also returning nothing, and that's why the loop continues, so I would expect that there is no possibility whatsoever that returning nothing could not continue the loop. Unless they want to force everyone to start returning something to keep the loop going, returning nothing has to be a way to keep it going.
jQuery.noop() can help
$(".row").each( function() {
if (skipIteration) {
$.noop()
}
else{doSomething}
});

Categories