I have an object which stores department hierarchy. Each department might have sub department as well. I am trying to loop to check all department and also sub(child) department properties are Open.
However, whenever I hit recursive call, it only iterates once and jump directly to return true, even though there are still some items which has not checked in the loop yet.
validateDepartment(departmentHierarchy: any) {
for (let dept of departmentHierarchy.children) {
if (dept!= undefined && dept!= null) {
if (dept.instance.status == "Open")
{
continue
}
else
{
if (dept.children != undefined && dept.children != null) {
this.validateDepartment(dept);
}
else {
return false
}
}
}
}
return true
}
Not part of any answer, but it helps to only write the code that "does" things, rather than having lots of code that does "what the code would already do anyway", such as calling a continue when the iteration code is a single if/else. We can rewrite your code to this, and have something easier to work with:
validateDepartment(tree: any) {
// step 1: do any validation of the top node
if (!validateTopNodeOnly(tree)) {
// this is a stop condition.
return false;
}
if (!tree.children) {
// this is also a stop condition, but for a different reason.
// a department without children should not _necessarily_ be invalid.
return true? return false? probably return true since the node itself is fine.
}
if (tree.instance && tree.instance.status !== "open") {
// and this is a third condition, but for yet another reason.
// I'm guessing this is also not "invalid", just means we shouldn't recurse.
return true? return false? probably return true as well.
}
// Then, get all (non-falsey) children,
let children = tree.children.filter(e => e);
// and iterate over them:
for (let e of children) {
let result = this.validateDepartment(e);
// cut your run short if validation fails
if (result === false) {
return false;
}
}
// and this is the expected stop condition for a normal run.
return true;
}
But using true/false is incredibly naive and won't tell you anything about where validation failed, so you'll want to work in "what failed", typically by returning a reference to the actual "thing that's getting validated" so that if your function returns true, all is well, and it returns something !== true then you know it failed, and the thing it returned is the department where things went wrong.
Also note that by using an early return on validation failure, you're missing out on information: instead it's way better to use .map() and construct a running tally of all deparments that pass/fail validation, so that you return an array in which either result.every(e => (e===true)) is true, or is false, in which case result.filter(e => (e!==true)) gives you the set of every single failed department.
isopen = this.validateDepartment(this.departmentHierarchy);
validateDepartment(dept: any): boolean {
let result=(dept.instance.status == "Open");
if (result) {
if (dept.children) {
dept.children.forEach(x => {
result = result && this.validateDepartment(x)
})
}
}
return result;
}
Related
I've searched for a couple stackoverflow questions although couldn't find my answer.
I'm trying to break from an else if statement and was wondering if there was a more efficient way.
Heres a snippet:
var argument = "something";
if(argument == 'not_this'){
// this doesn't trigger
} else if(argument){
// this triggers although if the functions in here doesn't match what I want,
// how do I make It skip to the else statement without adding another else
// if statement?
} else {
// do something if both statements above fail
}
Is there something that I can do which exits from the else if(argument)... without adding another else statement? I've tried using switch and case although those don't seem to help.
Thanks.
You could set a flag that is by default true, and whenever the argument is valid you set it to false.
Then, to know when to execute the 'else' block, you can just check whether the flag is true or not:
var argument = "something";
let invalid = true
if (argument == 'not_this') {
// this doesn't trigger
invalid = false
} else if (argument) {
if (typeof argument != 'string') {
//valid
invalid = false
}
}
if (invalid) {
console.log('invalid')
}
Restructure the code to avoid confusing states, move out the if 'has value' check.
if (argument) {
if (argument === 'not_this') {
// this doesn't trigger
}
} else {
// do something if both statements above fail
}
For equality, it is safer to use strict equal === instead of loose equal ==
You could try using return; to break out of the statement, as that would stop all other code from being read.
I'm new to Javascript. I started with a very basic project in Angular i.e. Form validation. In my case I've to call my custom method, validationTest() within itself only once. If I do not put any break condition then there will be too many recursions. I have to stop this. I tried many other solutions:
Break Statement in TypeScript
TypeScript - Loops
I followed them very carefully, but I'm getting this:
Module parse failed: Unsyntactic break (84:12)
Here's my code:
validationTest() {
let count =0;
this.isAnyRangeInvalid = false;
this.monthpicker.forEach(picker => {
if (picker.isValidRange === false) {
this.isAnyRangeInvalid = true;
}
});
count ++;
if(count===1) {
break;
}
this.validationTest();
}
Even VScode editor is also showing a red zig-zag line under the token break.
I'm coming from Java and CPP background. Please correct me.
To stop a function from executing, use return. break only makes sense in the context of a for or while loop.
But another problem is that your validationTest doesn't have a persistent view of the count variable - it's completely local, so the test isn't going to work anyway. Consider passing a parameter instead, which will indicate whether the current call is recursive or not:
validationTest(lastTry = false) {
this.isAnyRangeInvalid = false;
this.monthpicker.forEach(picker => {
if (picker.isValidRange === false) {
this.isAnyRangeInvalid = true;
}
});
if (!lastTry) this.validationTest(true);
}
Make sure that the initial call of validationTest doesn't pass a parameter, or passes false.
For a more general solution of limiting yourself to N recursive tries, you can pass around a number instead, eg:
validationTest(triesLeft = 3) {
this.isAnyRangeInvalid = false;
this.monthpicker.forEach(picker => {
if (picker.isValidRange === false) {
this.isAnyRangeInvalid = true;
}
});
if (triesLeft !== 0) this.validationTest(triesLeft - 1);
}
Consider this code:
function reason(id) {
var reason = prompt("State reason");
while (!reason.trim()) {
reason = prompt("Please enter text");
}
document.getElementById(id).value = reason;
return true;
}
It works perfectly fine, but when I want to get rid of the poppup by pressing escape for example, the function returns true because the form executes. What should I do to make it do nothing if I close/cancel the poppup?
... the function returns true because the form executes. What should I do to make it do nothing if I close/cancel the poppup?
It depends entirely on how you call your reason function, but if you want reason to return false when prompt is cancelled, then:
function reason(id) {
var reason = prompt("State reason");
while (reason !== null && !reason.trim()) { // *** Changed
reason = prompt("Please enter text");
}
if (reason === null) { // *** Added
return false; // *** Added
} // *** Added
document.getElementById(id).value = reason;
return true;
}
prompt returns null when you cancel it.
But again, it's up to what calls reason to do something appropriate with the true or false.
Side note: You can call your function and a variable inside it by the same name, but it's not a great idea. If it's a habit, you'll end up making writing recursive functions quite difficult...
It's pretty simple:
var shouldDoThis = function(query) {
documents.forEach(function(section) {
section.words.forEach(function(word) {
if (word.content == query.content) {
return true;
}
});
});
return false;
};
This is a (poorly) reworded snippet - if I pass in a query that should resolve to true, 'return true' gets hit but then jumps right to return false, so this always evaluates to false. What am I doing wrong?
Because you are returning false always. return true is on other scope.
You should write your code like this:
var shouldDoThis = function(query) { // 1st level
var should;
documents.forEach(function(section) { // 2nd level
section.words.forEach(function(word) { //3rd level
if (word.content == query.content) {
should = true;
return; // you "quit" the 3rd level function. This returns to 2nd level
}
}); // end of 3rd level
}); // end of 2nd level
return should;
}; // end of 1st level
More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope
If you were to logically break these up, it might make more sense. Although not necessarily JavaScript syntax, imagine this:
function shouldDoThis(query) {
documents.forEach(sectionDo);
return false;
}
function sectionDo(section) {
section.words.forEach(wordDo);
}
function wordDo(word) {
if (word.content == query.content) {
return true;
}
}
Now I know this wouldn't work in a real situation, but breaking it apart helps separate the idea of having multiple functions within functions. As had been mentioned, the return true; statement only applies to the wordDo function, not the shouldDoThis function.
A good solution could involve returning something from wordDo, sectionDo, and then checking that in shouldDoThis.
I want to use return false to break a .each() but also return a value at the same time. How can I do this?
Please refer to a work-around function to see what I am trying to do:
function HasStores(state) {
var statehasstores = false;
$(stores).each(function (index, store) {
if (state == store.state && store.category == "meyers") {
statehasstores = true;
return false; // break
}
});
return statehasstores;
}
What Id like to do in pseudo code is:
Function () {
for() {
if found {
return true;
}
}
return false;
}
You're doing it right...
Quote from http://api.jquery.com/each/
"We can stop the loop from within the callback function by returning false."
Be creative:
try {
$(stores).each(function (index, store) {
if(state == store.state && store.category == "meyers"){
throw store;
}
});
}
catch(e) {
// you got e with the value
}
No, I was just kidding, don't use this :). It came as an idea I liked to share.
Use a variable outside the loop to get the value and use it afterward.
var value;
$(stores).each(function (index, store) {
if(state == store.state && store.category == "meyers"){
statehasstores = true;
value = store; // added line
return false; //break
}
});
alert(value);
The way you're doing is just fine. I've tested on jsFiddle, see an example here.
It's not working for you? Can you show more context?
jQuery .each
Alternatively, you could use a for loop instead of each(), and just return the value.
What you're suggesting is the way to do it. I'd think of it less as a workaround and more as an idiom.
How about:
$.each( myObj, function( key, value ){
...
if( sthg... ){
myObj.somethingWentHorriblyWrong = true;
return false;
}
});
if( myObj.somethingWentHorriblyWrong ){
// ... do something, not forgetting to go:
delete myObj.somethingWentHorriblyWrong;
}
PS I was initially interested in what $.each(... actually returns. As it says on the relevant JQ page, "The method returns its first argument, the object that was iterated", but in fact the solution doesn't even require that you use that fact...
PPS Need a function that returns a value? Wrap in an outer function of course.
Okay I guess there's a little doubt about this point so maybe I'm making it clearer here :
When jquery doc says : "We can stop the loop from within the callback function by returning false." and you do :
Function () {
for() {
if found {
return true;
}
}
return false;
}
This doesn't mean that you're function will return true when find the searched element. Instead, it will always return false.
So to make your function work as you whish I propose to do so :
Function () {
variable found = false;
foreach() {
if found {
found = true;
return false; // This statement doesn't make your function return false but just cut the loop
}
}
return found;
}
Of course there are many other ways to perform this but I think this is the simplest one.
Coopa - Easy !
As others have noted from jQuery Each, returning false will only break from the loop not return the value, returning true however will 'continue' and immediately begin the next iteration. With that knowledge, you could somewhat simplify your code like this:
function HasStores(state) {
var statehasstores = false;
$(stores).each(function (index, store){
// continue or break;
statehasstores = !(state == store.state && store.category == "meyers"))
return statehasstores;
});
return !statehasstores;
}
This of course is a little silly using the double negative, and has the side effect of saving 'true' to statehasstores for every false iteration and vice versa, however the end result should be the same and you no longer have that if statement.