Ng-Options Expression Repeatedly Called - javascript

I am having multiple issues with my <select> element in angular and am trying to understand what is going on. My first step is to understand why the multiple console.log() messages I have put in for debugging repeatedly appear in the console, as in, instead of the message appearing once like I would expect, they appear an infinite number of times, as if part of an infinite loop. Is this how a function called from an ng-options is supposed to behave? If so, I don't understand why, if not, then I would like to fix my loop.
My html: <select ng-options="theorderdate as theorderdate for theorderdate in getOtherOrderDates(Bread.text)" ng-model="randomDateRomantic.randomDateRomantic" ng-change="soRomantic(Bread.text)"></select>
The console.log() messages appear from the getOtherOrderDates() function, which is below (with my comments included):
$scope.getOtherOrderDates = function(loaf) {
var istheLoafdaily = false;
var theorderdates = [];
for (var i = 0; i < $scope.theBreadsList.length; i++) {
for (var z = 0; z < $scope.theBreadsList[i].breads.length; z++) {
if ($scope.theBreadsList[i].breads[z].text == loaf && i > 0) //not a daily loaf, goes beyond "Daily Breads"
{
console.log(theorderdates);
theorderdates = theorderdates.concat($scope.theBreadsList[i].breads[z].orderDates); //concat the matched bread's order dates
console.log(theorderdates, $scope.theBreadsList[i].breads[z].orderDates);
theorderdates = _.sortBy(theorderdates, function(m) {
return m.getTime()
});
for (var y = 0; y < theorderdates.length; y++) {
theorderdates[y] = theorderdates[y].toLocaleDateString();
}
theorderdates = _.uniq(theorderdates);
if (theorderdates.length > 0) {
console.log("Something is wrong here"); //problem
$scope.randomDateRomantic.randomDateRomantic = theorderdates[0];
}
console.log(theorderdates);
return theorderdates;
} else if ($scope.theBreadsList[i].breads[z].text == loaf && i == 0) { //a daily loaf, i == 0
console.log("The bread matched is daily", loaf); //***
istheLoafdaily = true;
console.log(theorderdates); //***
theorderdates = theorderdates.concat($scope.theBreadsList[i].breads[z].orderDates); // concat the matched bread's order dates
console.log(theorderdates, $scope.theBreadsList[i].breads[z].orderDates); //***
break; // escape the for loop, should it be two breaks?????? yes...
} else if (istheLoafdaily && i > 0 && $scope.theBreadsList[i].breads[z].orderDates.length > 0) { //not sure what scenario this matches, hence wtf
theorderdates = theorderdates.concat($scope.theBreadsList[i].breads[z].orderDates);
console.log("wtf");
}
}
}
//end of outermost for loop
//not sure what this is doing because this functionality is repeated up there^ (for non-daily breads)
theorderdates = _.sortBy(theorderdates, function(m) {
return m.getTime()
});
for (var y = 0; y < theorderdates.length; y++) {
theorderdates[y] = theorderdates[y].toLocaleDateString();
}
theorderdates = _.uniq(theorderdates);
if (theorderdates.length > 0) {
$scope.randomDateRomantic.randomDateRomantic = theorderdates[0];
console.log("Something is wrong here (daily)"); //problem
}
return theorderdates;
//not sure what this is doing because this functionality is repeated up there^ (for non-daily breads)
//if change to Locale date string then not unique, but if don't change then not a date to sort!!!!!!! >:(
},
I am getting almost all console messages an infinite number of times, without doing anything such as firing the ng-change function. I just add a daily bread to my cart for instance, and then the console gets filled with the following messages, that I have starred in my code.
My theBreadsList is not very long, so there is something going on that it is going repeatedly like this. Even if I broke out of the for loop twice as you will see in my code, it wouldn't explain the fact that it logs to the console all the time, because eventually the loop would not be satisfied, and this wouldn't take to long as has been mentioned.
Please advise, thank you. If you need more information, I am happy to provide.

The getOtherOrderDates will be called in each digest cycle so that angular knows whether to update options in select. That's most likely the reason you're seeing this method being called many times.
If you're worried about performance impact of this loop you can build the options upfront inside your controller store it in $scope like so:
$scope.options = $scope.getOtherOrderDates($scope.Bread.text);
whenever $scope.Bread.text changes and then use $scope.options inside your template.

To avoid triggering your loops in every digest loop you can use one time binding ::value.
<select ng-options="theorderdate as theorderdate for theorderdate in ::getOtherOrderDates(Bread.text)"
ng-model="randomDateRomantic.randomDateRomantic"
ng-change="soRomantic(Bread.text)"></select>
Thanks to that expression inside ng-options will be evaluated only once and the watcher will be removed after first evaluation which will stop your function being triggered in next digest loop iterations.
DOCS

Related

Add elements to an already evaluated loop

Good afternoon, I encounter the following problem, I am trying to loop through a list with subdirectories to add to an array (repositories) the routes of these subdirectories, this is my code:
for (n=0; n<=pendingRepos.length; n++){
subruta = pendingRepos[pendingRepos.length -1]
pendingRepos.pop()
c.list(subruta, function(err, sublist) {
if (sublist.length != 0){
for (g=0; g < sublist.length; g++){
if (sublist[g].type === 'd' ){
repositories.push(subruta+'/'+sublist[g].name)
pendingRepos.push(subruta+'/'+sublist[g].name)
}
else {files.push(subruta+'/'+sublist[g].name)}
}
}
});
}
For example when starting the loop for my array pendingRepos has the following structure:
pendingRepos = ['/ dir1 / dir2', / dir3 / dir4 ']
the loop is executed correctly 2 times and the last element was removed, but at the time of the other loop to add another 'last' element to the array the first for loop does not take it into account.
I understand that the condition was already evaluated before I added more elements, is this correct? How can I avoid it?
It looks like you're treating the array of pending repos in two contradictory ways. The outer for loop:
for (n = 0; n <= pendingRepos.length; n++) { ... }
is treating pendingRepos as an immutable list, going through from beginning to end and processing each element. (And not doing this correctly, either - we should be iterating to n < pendingRepos.length if this is the option we're using).
The logic immediately after the loop, however,
subruta = pendingRepos[pendingRepos.length -1]
pendingRepos.pop()
treats pendingRepos as a mutable stack, from which you would keep processing the last element until the stack was empty.
In order to correctly process the array, you need to choose one or the other. Since it seems that the rest of your code is correctly using the stack approach, the loop at the top should be changed to match, which in this case would simply be
while (pendingRepos.length > 0) { ... }
The end result will look as follows:
while (pendingRepos.length > 0){
const subruta = pendingRepos[pendingRepos.length -1]
pendingRepos.pop()
c.list(subruta, function(err, sublist) {
if (sublist.length != 0){
for (let g = 0; g < sublist.length; g++){
if (sublist[g].type === 'd' ){
repositories.push(subruta+'/'+sublist[g].name)
pendingRepos.push(subruta+'/'+sublist[g].name)
} else {
files.push(subruta+'/'+sublist[g].name)
}
}
}
});
}
EDIT: The above answer only works if c.list() is a synchronous function that immediately runs your callback before returning - however, since it is contacting an FTP server, it is not. This means that the entire while loop will finish before any of those callbacks run, and anything they add to pendingRepos will not be processed. In order to use asynchronous functions, you have to structure your function completely differently, basically using more and more asynchronous functions as far up as you can go.
Fortunately, doing that is pretty easy in this case. What you are doing with pendingRepos is conceptually known as depth-first search (or "DFS"), where you search through a tree structure by repeating the search at each subnode. Using the stack of pending directories is one way to do DFS, and another way to do it is to use a recursive function (basically repeating the search function each time you reach a directory).
Here's a possible implementation of that, with the use of callbacks extending all the way out.
// an outer function for the whole operation. You would provide
// a callback that takes the lists of repositories and files.
function getTheRepos(startList, callbackForWholeThing) {
// build up our lists of repositories and files
const repositories = [];
const files = [];
// keep track of how many calculations are running
let repoGetCount = 0;
// an inner function to run exactly one result
function getOneRepo(subruta) {
// at the start, say we're running
repoGetCount++;
c.list(subruta, function(err, sublist) {
if (sublist.length != 0){
for (let g = 0; g < sublist.length; g++){
if (sublist[g].type === 'd' ){
repositories.push(subruta+'/'+sublist[g].name)
// for each directory we find, call this inner function again.
// This is the critical part that makes this all work.
getOneRepo(subruta+'/'+sublist[g].name)
} else {
files.push(subruta+'/'+sublist[g].name)
}
}
// at the end, say we're not running,
// and call the whole callback if we're the last one
repoGetCount--;
if (repoGetCount === 0) {
callbackForWholeThing(repositories, files);
}
}
});
// now that we have the function, run it on each of our
// start directories to start things off
for (let n = 0; n < startList.length; n++) {
getOneRepo(startList[n]);
}
// the cogs are in motion, so now return.
// The callback will be called when the tree has been searched.
}

if else preventing loop for passing through object

I am having trouble returning a statement when my RegExp finds no matches :
function ai(message) {
if (username.length < 3) {
username = message;
send_message("Oh, well hello " + username + ", My name is Donald J Trump and i'm a big boy");
} else {
for (i = 0; i <= botChat.length; i++) {
var re = new RegExp(botChat[i][0], "i");
if (re.test(message)) {
var length = botChat[i].length - 1;
var index = Math.ceil(length * Math.random());
var reply = botChat[i][index];
send_message(reply);
}
}
}
}
When I enter a phrase it can correctly match the first line in an array as per the for loop. The issue I'm having is when I try to add an else statement it ceases to loop through my array properly.
I have tried :
else if (re.test(message) === false) {
send_message("i can't be expected to know everything");
}
But it doesn't work, it prevents the loop from looping past botChat[0][0].
I've also tried :
if (send_message().val() == "") {
// ...
}
But every time my code no longer works. Is there a method of adding something to my array that says 'if you don't find a match above, choose this'?
Or just a way of working my code so that the if/else works?
attached is the codepen.
I checked your codepen and the lines 190 and 194 console.log(send_message().val()); seems to be breaking the loop because those lines are throwing an exception since send_message() returns undefined and undefined does not have the .val() method.
Your regular expressions are working fine. I recommend not creating new RegExp objects every iteration, instead, use the one defined in the array if (botChat[i][0].test(message)), the overhead will be less.

Two conditions in for loop

I have this code which im echoing data from jquery success .
for(i = 0;i < dat.id.length; i++){
// im echoing data here
}
and i want check for the highest value in dat.voteup
like that (It works)
var maxvoteup = 0;
if (dat.voteup[i] - dat.votedown[i] ==maxvoteup) {
console.log(maxvoteup);
//i want fetch maxvoteup here
}
But i want make just one For loop not two . How can i do that pls?
EDIT:
var maxvoteup = 0;
for (i = 0;i < (dat.voteup-dat.votedown).length ;i++) {
if (dat.voteup[i] - dat.votedown[i]>maxvoteup) {
maxvoteup = dat.voteup[i] - dat.votedown[i];
console.log(maxvoteup);
//i want fetch maxvoteup here
}
}
EDIT2:
this is what im getting in my jquery response , But actually my max vote is 10 not 8 . 8 is just voted up to 8 , but there is other votes which is 10.
{"status":"success","message":[],"date_edited":[2016-04-21 21:31:25],"voteup":[],"votedown":[8],"id":[]}
Your code is a little hard to read, but if I understand correctly I believe all you need to do is combine both into a single loop with maxvoteup defined outside.
var maxvoteup = 0;
for(i = 0;i < dat.id.length; i++){
// echo data...
// max upvote
if (dat.voteup[i] - dat.votedown[i] == maxvoteup) {
console.log(maxvoteup);
}
}
EDIT:
Unfortunately getting the maximum value from a list requires you to iterate through the list, i.e., a maximum value cannot be found in constant time. I suggest you first find the maximum, and then proceed with your for loop.
Also, if you know your maximum is a list of numbers, you can actually use Javascript's apply to make the code a little cleaner:
var maxvoteup = Math.max.apply(Math, listOfMaxVotes);
See here: How does the Math.max.apply() work?
EDIT2:
If you want to continuously keep track of the maximum, then all you need to do is move the maxvoteup variable to outside of your response handlers so you can always keep track.
// global scope...
var maxvoteup = 0;
// your jquery response handler
var onSuccessData = function(data) {
// get the highest possible max from the `voteup` and `votedown` lists
var responseMax = Math.max.apply(Math, data.voteup.concat(data.votedown));
// check if it's greater than our global maxvoteup
if (responseMax > maxvoteup) {
// if it is, then update our global `maxvoteup` to the new value
maxvoteup = responseMax;
}
// continue with your other processing...
};

Simple arithmetic challenge function with limited attempts

Recently began studying Javascript, trying to read out of Javascript: The Definitive Guide and Eloquent Javascript, while going off on my own to experiment with things in order to really etch them in my memory. I thought a good way to get my head around arithmetic operations and conditional statements, I'd build a series of little games based around each Math operator, and began with addition.
function beginAdditionChallenge() {
var x = Math.ceiling(Math.random()*100);
alert(x);
for (var i = 0; i < 3; i++) {
var a = Number(prompt("Provide the first addend.", ""));
var b = Number(prompt("Provide the second addend.", ""));
if (a + b === x) {
alert("Well done!");
break;
}
else if (a + b !== x && i < 3) {
alert("Please try again.");
}
else {
alert("Fail.");
}
}
}
function initChallenge() {
var button = document.getElementById("challengeButton");
button.addEventListener("click", beginAdditionChallenge);
}
window.addEventListener("load", initChallenge);
You can see the whole thing thus far on JSFiddle, here. The idea is that clicking the button generates a random number between 1 and 100, displays it to the user, then prompts them to provide two addends, giving them 3 attempts. If the sum of these addends is equal to the RNG number, it congratulates the user and ends the program. If they do not provide suitable addends, the loop prompts them to try again, until they've hit 3 attempts, at which point the program snarks at them and ends.
I know the event listener is not the failure point here, as when I change beginAdditionChallenge to simply display a test alert, it works, but I don't know what exactly is wrong with the loop I've created.
You did it correctly. However, Math.ceiling isn't a function and should be Math.ceil. In addition, your code (in jsfiddle) should be set to wrap in head. Why? Because right now you call initChallenge when the page loads. However, in your jsfiddle example, the code runs onLoad so the load event never gets called. Essentially, you're adding a load event after the page has loaded.
http://jsfiddle.net/rNn32/
Edit: In addition, you have a for loop that goes up to three. Therefore
else if (a + b !== x && i < 3) {
alert("Please try again.");
}
should be
else if (a + b !== x && i < 2) {
alert("Please try again.");
}
because when i === 2, the user's last chance has ended.
Everything is fine. Just change:-
var x = Math.ceiling(Math.random()*100);
to:-
var x = Math.ceil(Math.random()*100);

Javascript, loops infinitely and freezes

So I'm making a simple physics simulation using HTML5 canvas and javascript. I'm trying to make some realistic collisions, but whenever a collision occurs the code begins to loop infinitely and freezes the page.
I am using Google Chrome 24.0.1312.32 beta-m
When looking at the javascript console the line "console.log("I am colliding with something")" goes crazy and is printed thousands of times per second and completely breaks the page.
I'm not really sure why its' happening and I have no idea what to do. Any help and/or input would really be appreciated.
for (i = 0; i <= 3; i++) {
if (collide(i)) {
console.log("I am colliding with something");
if (typeof getCollideIndx === 'undefined') {
console.log("collide index is not undefined");
if (!getCollideIndx(i)) {
console.log("Made it past null check");
//...update object based on collision
the collide() function is:
function collide(b) {
for (i = 0; i <= 3; i++) {
//Distance between each object
var distance = (Math.sqrt(Math.pow((balls[b].x - balls[i].x), 2) + Math.pow(balls[b].y - balls[i].y, 2)));
if (distance < 32) {
//must be less than 2*radius -- all radii are the same
//makes it so that it doesn't return true when checking its own index
if (!(balls[b].mass == balls[i].mass)) {
return true;
} else {
return false;
}
}
}
}
I cannot see an infinite loop in your code , my best guess would be this statement
if (typeof getCollideIndx === 'undefined')
fails every time and whatever function the below code is in is called continuously
for (i = 0; i <= 3; i++) {
if (collide(i)) {
console.log("I am colliding with something");
if (typeof getCollideIndx === 'undefined') {
console.log("collide index is not undefined");
if (!getCollideIndx(i)) {
console.log("Made it past null check");
//...update object based on collision
also in this , I do not see the point of the for loop as the control always goes back to the calling function (in the collide() function)
Well, being trapped in that for loop implies that the index variable i is getting set incorrectly somewhere. Without being able to see the entirety of the code I cannot say for sure, but the loop for(i=0; i<3; i++){... will be assigning i to the window object because you have not explicitly scoped it (i.e. for(var i...).
See for example this jsfiddle where the first function will only run once rather than three times because the same variable i is affected in the second function.
Obviously running once != infinite loop, but if the collide function does something to i (or maybe breaks after it finds a collision?) then the value of i will be reset to 0 at the beginning of its for loop each time it's called.
So yeah without some more code I can't say for sure; but my advice in this case is: always use var in for loops or weird things can happen!

Categories

Resources