How to exit a loop inside a functionin Javascript - javascript

I am trying to exit a function call when a value is found, and I can not wrap my head around this.
Using the debugger I've noticed that the return found; line makes execution jump to the end of the function, OK... but after that another iteration on the for loop is started.
I've tried using break too.
My code is below.
Edited
I've checked the logic on the debugger, adding a breakpoint on found = true and it works fine, so the only thing left is that it should exit properly after that...
// Tree traversal until answer found via answer.id
function locateAnswer(wholeTree, answerID) {
var found = false;
if (wholeTree.answers) { // checks if next_queston is populated first
for (var i = 0; i < wholeTree.answers.length; ++i) {
if (wholeTree.answers[i].id == answerID) {
console.log("found!");
found = true;
return found; // TRIED break; TOO
} else {
for (var j = 0; j < $scope.breadcrumbs.length; ++j) {
if ($scope.breadcrumbs[j].question == wholeTree.question) {
// if already exist, we pop elements until it does not exist anymore
while ($scope.breadcrumbs[j]) {
$scope.breadcrumbs.pop();
}
}
}
// we push the new breadcrumb
$scope.breadcrumbs.push({ "question": wholeTree.question, "answer": wholeTree.answers[i].answer });
locateAnswer(wholeTree.answers[i].next_question, answerID);
}
}
}
// ALSO TRIED return HERE AFTER break
};

You should use break inside the loop and return statement at the end of the function. Please updated code
function locateAnswer(wholeTree, answerID) {
var found = false;
if (wholeTree.answers) { // checks if next_queston is populated first
for (var i = 0; i < wholeTree.answers.length; ++i) {
if (wholeTree.answers[i].id == answerID) {
console.log("found!");
var found = true;
break; // TRIED break; TOO
} else {
for (var j = 0; j < $scope.breadcrumbs.length; ++j) {
if ($scope.breadcrumbs[j].question == wholeTree.question) {
// if already exist, we pop elements until it does not exist anymore
while ($scope.breadcrumbs[j]) {
$scope.breadcrumbs.pop();
}
}
}
// we push the new breadcrumb
$scope.breadcrumbs.push({ "question": wholeTree.question, "answer": wholeTree.answers[i].answer });
locateAnswer(wholeTree.answers[i].next_question, answerID);
}
}
}
// ALSO TRIED return HERE AFTER break
return found;
};

Try to use break instead of return inside the for loop

Related

Javascript function error : length undefined

I am a novice JavaScript user learning about how to code functions in a sustainable and clean way.
But I came across some problems and it throws an error such as console undefined or length undefined and I don't know why it happens like that.
//objects
var lists = [{
ignore: true,
accept: true
},
{
meaning: true
}
];
var start1 = processthings(lists, start);
if (!start1) {
console.log("wrong! start it first!")
};
var dictionary1 = processthings(lists, dictionary);
if (!dictionary1) {
console.log("look it up!")
};
//comprehensive process function
function processthings(lists, cfunctions) {
for (var i = 0; i < lists.length; i++) {
if (cfunctions(lists[i])) {
return true;
};
return false;
};
};
//each function : number 1
function start(element) {
return (element.ignore == true);
};
// each functon : number 2
function dictionary(element) {
return (element.meaning);
};
The for loop in function processthings will never iterate through the entire list. The function will always return after the first iteration.
I am not sure whether that is done intentionally or not. But I think the function should be modified as below -
//comprehensive process function
function processthings (lists,cfunctions){
var flag = false;
for (var i=0; i< lists.length; i++){
if (cfunctions(lists[i])){
flag = true;
break;
};
};
return flag;
};
See the working code here

Execute the else statement if none of the items are found

I have this for loop, and I would like to execute the else in the for loop only if none of the if conditions are met. The for loop runs until every item in the database has been checked. If none of the items in the database matches the user input then I want to run the else.
Right now, it runs the else right after the first try which means if the item matches is in the last row, it will just throw it in the error page since it stops the evaluation at the first iteration.
for(var i=0; i< rows.length; i++) {
if (rows[i].hashtag == userEnteredHashtag) {
// Display the choose Box page
res.render('chooseBox', {});
}
else {
// Display the invalid hashtag page
res.render('invalidHashtag', {});
}
}
Just move the else portion outside of the loop and execute it based on a flag
var wasFound = false;
for (var i = 0; i < rows.length; i++) {
if (rows[i].hashtag == userEnteredHashtag) {
// ...
wasFound = true; // set the flag here
}
}
if (!wasFound) {
res.render('invalidHashtag', {});
}
So add a check outside.
var hasMatch = false;
for (var i = 0; i < rows.length; i++) {
if (rows[i].hashtag == userEnteredHashtag) {
// Display the choose Box page
res.render('chooseBox', {});
hasMatch = true;
}
}
if (!hasMatch) {
// Display the invalid hashtag page
res.render('invalidHashtag', {});
}
Create a variable to track whether your condition has been met:
var isValid = true;
for(var i=0; i< rows.length; i++) {
if (rows[i].hashtag != userEnteredHashtag) {
isValid = false
}
}
isValid ? res.render('chooseBox') : res.render('invalidHashtag')
Another way to do it is to use filter and forEach.
var rows = [{hashtag: '#a'}, {hashtag: 'b'}, {hashtag: 'c'}];
var userEnteredHashTag = '#a';
var matchingRows = rows.filter(row => row.hashtag === userEnteredHashTag);
if (matchingRows.length) {
matchingRows.forEach(row => console.log(row));
} else {
console.log('invalid');
}

Javascript Recursion returning undefined

I'm struggling in a recursive Javascript function to find a specific subdirectory. This is my code:
function navigateToParent() {
var parentFullPath = parentDirectory(); // gets the full Path String
if (parentFullPath != null) {
var parent = getDirectoryByName(parentFullPath, rootDirectory);
// set the parent directory object as the current one
currentDirectory(parent);
}
}
function getDirectoryByName(fullName, myDirectory) {
if (myDirectory.fullName == fullName) {
return myDirectory;
} else {
var subs = myDirectory.subDirectories;
for (i = 0; i < subs.length; i++) {
return getDirectoryByName(fullName,subs[i]);
}
}
}
Every directory object has the properties fullName(string),subDirectories(array of directories) and files(array of files). My aim is to get the correct directory object, where it's fullName is matching.
I know, that i have to break the for loop in some way, but i don't know how to do it exactly.
After overthinking the logic i came to this solution (seems to work):
function getDirectoryByName(fullName, myDirectory) {
if (myDirectory.fullName == fullName) {
return myDirectory;
} else {
var subs = myDirectory.subDirectories;
for (i = 0; i < subs.length; i++) {
var match = getDirectoryByName(fullName, subs[i]);
if (typeof match !== "undefined"){
return match;
}
}
}
}

Get only one match with regexp

In the function below I iterate through an array (incidents) which contains of strings. The strings is describing an incident (crime or accidents) that is scrapted from another web app, and what I'm doing is dividing and counting the different crimes / accidents and placing them in an object (INCIDENT_MATCHES).
However, some of the text strings may contain of several of the keywords that I search for (e.g. both "gunfire" and "battery"), but that I don't want. Instead I just want the first found word to be counted, and if more keywords are found they should be ignored.
How could this be done?
var INCIDENT_MATCHES = {
battery: /\w*(bråk)\w*|överfall|slagsmål|slogs|misshandel|misshandlad|\w*(tjuv)\w*/ig,
burglaries: /snattade|snattare|snatta|inbrott|bestulen|stöld|\w*(tjuv)\w*/ig,
robberies: /\w*(rån)\w*|personrån|\w*(ryckning)\w*|väskryckt*/ig,
gunfire: /skottlossning|skjuten|sköt/ig,
drugs: /narkotikabrott/ig,
vandalism: /skadegörelse|klotter|\w*(klottra)\w*/ig,
trafficAccidents: /(trafik|bil)olycka|(trafik|bil)olyckor|\w*(personbil)\w*|singelolycka|kollision|\w*(kollidera)\w*|påkörd|trafik|smitningsolycka/ig,
};
var j = 0,
incidentCounts = {},
incidentTypes = Object.keys(INCIDENT_MATCHES);
incidents.forEach(function(incident) {
matchFound = false;
incidentTypes.forEach(function(type) {
if(typeof incidentCounts[type] === 'undefined') {
incidentCounts[type] = 0;
}
var matchFound = incident.match(INCIDENT_MATCHES[type]);
if(matchFound){
matchFound = true;
incidentCounts[type] += 1;
}
});
j++;
});
You can return false from the "each" handler to stop iteration.
if(matchFound){
matchFound = true;
incidentCounts[type] += 1;
return false;
}
edit — and you'll want (I think) another test outside that, at the end of the outer loop:
j++; // I don't understand what that does ...
if (matchFound) return false;
I found this solution below to work. What I did was the following:
I replaced the second forEach statement with "every"
Put "return false" inside "if(matchFound)"
Added "else { return true; }" so that the loop continues if no match is found.
The code:
incidents[2].forEach(function(incident) {
matchFound = false;
incidentTypes.every(function(type) {
if(typeof crimesPerType[type] === 'undefined') {
crimesPerType[type] = 0;
}
var matchFound = incident.match(INCIDENT_MATCHES[type]);
if(matchFound){
crimesPerType[type] += 1;
if (type == 'trafficAccidents') {
incidents[3][j].push('traffic');
}
else {
incidents[3][j].push('crime');
}
return false;
}
else {
return true;
}
});

Javascript: What is wrong with this conditional?

I'm working on a Chrome extension an I've hit a wall.
function isInQueue(id) {
chrome.extension.sendRequest({getQueueItems: 1}, function(response) {
var items = response.items;
if (items) {
for (var i = 0; i < items.length; i++) {
if ((items[i].id == id) == true) return true;
}
return false;
} else { return false; }
});
}
The request returns 'items' which is an array of objects. I am trying to see if another item outside of the queue already exists inside the queue. For example, there is an item on the outside with an id equal to '198677'. I know I already have an exact copy of that same item in my queue with the exact same id, '198677', however, when I test the two for equality (items[i].id == id) == true, it returns false. I have checked the typeof both and they are both strings. I have tried using === and that hasn't worked. I tried adding zero to each of them to turn them into integers and that made the function return true when it was actually true, however, when I tested for true if (isInQueue(id) == true) the conditional returned false.
This is all very confusing and frustrating for me. They're both strings, why doesn't it work?
Help is very much appreciated.
The problem is chrome.extension.sendRequest is asynchronous - it returns immediately, but the callback function you provide to it will only be called once the request has completed.
The usual way to handle something like this is to pass a callback to your isInQueue method; the callback is called when the asynch operation is completed with the result.
function isInQueue(id, callback) {
chrome.extension.sendRequest({getQueueItems: 1}, function(response) {
var result = false;
var items = response.items;
if (items) {
for (var i = 0; i < items.length; i++) {
if ((items[i].id == id) == true) {
result = true;
break;
}
}
}
callback(result);
});
}
I figured it out:
function isInQueue(id) {
var result = false;
var queue = localStorage["queue_rss"];
if (queue != undefined) {
var items = JSON.parse(queue).items;
if (items) {
for (var i = 0; i < items.length; i++) {
if ((items[i].id == id) == true) {
result = true;
break;
}
}
}
}
return result;
}
I should have done it that way in the first place.
Thanks guys. :D

Categories

Resources