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}
});
Related
I know this might be basic and simple but since I am self-learning, so I wanted to know what was wrong and I thought this is the best place to learn from.
I am trying to write a recursive code that returns true or false. The condition to check is if the set of words can make the given target word.
The error I keet getting is :
if (targetString.indexOf(dictionary[i]) == 0) {
^
RangeError: Maximum call stack size exceeded
at String.indexOf (<anonymous>)
I am pretty sure that the problem with code is in a way I am returning because I always find it confusing.
my code is:
let targetString = "furniture";
let dictionary = ["fur", "ure", "nit"];
const tableData = {};
const canConstructRecursive = (targetString, dictionary) => {
if (targetString == "") {
return true
}
for (let i = 0; i < dictionary.length; i++) {
if (targetString.indexOf(dictionary[i]) == 0) {
shorterTargetString = targetString.slice(0, dictionary[i].length);
return canConstructRecursive(shorterTargetString, dictionary);
}
}
return false;
}
console.log(canConstructRecursive(targetString, dictionary));
I am learning recursion and from time to time I feel I don't understand the logic of return to next/previous recursive call.
I would really appreciate it if someone could help me with what I am doing wrong and change my way of thinking.
My way of thinking is that:
the base case is returned if it is reached at that stage otherwise loop go through all the option and the inner node or upper stack need to return value to lower stack so I am doing return canConstructRecursive() inside for. If even in all options which is all iteration of for loop, it is not returned, there is return false at the end.
Thank you in advance
The reason is that although your variable is named shorterTargetString, it is not guaranteed to be really shorter. If i is the index of the shortest word in dictionary, then there is no way your string will ever get shorter by recursing with it.
The mistake is that the slice should not start at 0, but after the part that was matched, so remove the first argument from the slice call.
This will solve the stack overflow error.
Secondly, if the recursive call returns false you should not give up, but keep trying with the next word. So only return out of the loop when you got true from recursion:
let targetString = "furniture";
let dictionary = ["fur", "ure", "nit"];
const tableData = {};
const canConstructRecursive = (targetString, dictionary) => {
if (targetString == "") {
return true
}
for (let i = 0; i < dictionary.length; i++) {
if (targetString.indexOf(dictionary[i]) == 0) {
shorterTargetString = targetString.slice(dictionary[i].length);
if (canConstructRecursive(shorterTargetString, dictionary)) {
return true;
};
}
}
return false;
}
console.log(canConstructRecursive(targetString, dictionary));
More on the second fix.
Your code will unconditionally return the value of the recursive call, even when it is false. This is not good: in case the recursive call returns false, the caller should continue with its for loop to try alternatives.
Let's for instance add a word to your example dictionary: you'll agree that adding a dictionary word should not change the outcome for the input "furniture". So here it is:
["furn", "fur", "ure", "nit"]
But surprise: your code now returns false for "furniture"! This is because "furn" mathes, but the recursive call with "iture" as first argument does not find further matches, so it returns false, and now the caller also returns false. This is wrong. It should give up on "furn", but not on the whole exercise. It should have continued and tried with "fur". This is why the exit out of the for loop should only happen upon success, not upon failure. Failure can only be confirmed when all dictionary words have been tried, so the for loop must continue for as long as there is no recursive success.
User trincot already explained what was wrong with your code. Here, I just want to point out that your structure, which is something like for (...) {if (...) { if (...) {return true} } } return false, might be better handled with Array.prototype.some and an && statement. Combining this with the fact that t .indexOf (s) == 0 might more clearly be expressed as t .startsWith (s), and sprinkling in a conditional statement instead of an if statement, we can arrive at what I think is a more elegant formulation:
const canConstruct = (t = '', ss = []) =>
t == ''
? true
: ss .some ((s) => t .startsWith (s) && canConstruct (t .slice (s .length), ss))
console .log (canConstruct ('furniture', ['fur', 'ure', 'nit'])) //=> true
console .log (canConstruct ('furniture', ['furn', 'fur', 'ure', 'nit'])) //=> true
console .log (canConstruct ('banana', ['b', 'ana'])) //=> false
console .log (canConstruct ('banana', ['ba', 'na'])) //=> true
/*please have a look at the following function. Its a simple function. I want to iterate over the movies array and return the element; only if the element's title is exactly same as the argument passed in. otherwise return false at the end of the iteration.
The problem is, it always return false. However, if I use a regular forloop instead of forEach loop, it works perfectly fine.. can someone please explain why is this situation?????? Thank You in advance.
*/
function searchMovies(title) {
movies.forEach(function(ele){
if(ele.title === title){
return ele;
}
});
return false;
}
//movies array
var movies = [
{title: 'The Hobbit'},
{title: 'The Great Gatsby'},
{title: 'Gone with the Wind'}
];
//the following line calls the function
searchMovies('The Great Gatsby');
You're returning from inside the callback passed to forEach which forEach ignores every time and call the callback to the next element. What you need is to use find like this:
function searchMovies(title) {
var found = movies.find(function(ele){
return ele.title === title;
});
return found; // found will be either and item from the array (if find found something) or undefined (if it doesn't)
}
Note 1: the movies array should be either defined before the function searchMovies, or passed to it as a parameter (the best approach).
Note 2: if you want to return an array of all the matched element (if there is duplicates in the array and you want to return all of them), then use filter, which is used the same way and it return an array of all the matched elements (an empty one if nothing matched).
Because you're returning inside the forEach function.
function searchMovies(title) {
var foundMovie = false;
movies.forEach(function(ele) {
if (ele.title === title) {
foundMovie = ele;
}
});
return foundMovie;
}
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.
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;
}
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]