Javascript: Iterating over an array causes an infinite loop? - javascript

I'm trying to iterate over some data in Javascript, using the following code:
for (var i = 0; i < fromdata.length; i++) {
var mainid = fromdata[i].id;
var sub = afcHelper_Submissions[mainid];
/* do more stuff */
fromdata is an array of Objects that looks something like this:
[{ type="ffu", to=" Jon Corzine ", id=1, more...}, { type="ffu", to=" Jon Corzine ", id=2, more...}]
As you can see, I just want to get each object's id and store it to mainid and then do some more with it; however, I run into trouble: looping! Looping! Looping! The loop keeps running again and again. It never stops, and just manages to freeze up Firebug.
Update: Here's the "do more stuff", in all its pastebin glory: http://pastebin.com/Mfr90uq7. Note that I changed the variable name from sub to sub_m to avoid a potential conflict, but the problem persisted.

A loop only can be infinite if the condition is always true. In your case it looks that it should reach a false , but provably formData is getting new elements each iteration of the loop or i is being modified and returned to previous values.
What I recomend is to create variables that will be used only for comparasion purposes:
var max = fromdata.length;
for (var count = 0; count < max; count++) {
var i = count;
// your stuff using formdata and i
Now max and count will not be modified by the code in the loop and the loop will reach an end.

Related

Javascript Nested Loop Pushing to Array

I am relatively new to programming and am having some issues with a project I am working on.
msg.newCG2 = [];
for(i=0;i<msg.newCG.length;i++){
for(j=0;j<msg.campaignGroup.length;i++){
if(msg.campaignGroup[j].col10 === msg.newCG[j]){
msg.groupTotals = msg.groupTotals + msg.campaignGroup[j].col11;
}
msg.newCG2.push(msg.newCG[i], msg.groupTotals)
}
}
Basically, for each one of the "IDs" (integers) in msg.newCG, I want to look for each ID in msg.campaignGroup and sum up the totals for all listings with the same ID, from msg.campaignGroup.col11 - then push the ID and the totals to a new array - msg.newCG2.
When I run the code, the first item sent through processes, but grinds to a halt because of memory. I assume this is because of an error in my code.
Where did this code go wrong? I am sure that there are better ways to do this as a whole, but I am curious where I went wrong.
There is a typo in your second for loop and the push needs to happen inside the outer loop.
msg.newCG2 = [];
for(i=0;i<msg.newCG.length;i++){
for(j=0;j<msg.campaignGroup.length;j++){
if(msg.campaignGroup[j].col10 === msg.newCG[i]){
msg.groupTotals = msg.groupTotals + msg.campaignGroup[j].col11;
}
}
msg.newCG2.push(msg.newCG[i], msg.groupTotals)
}
How about:
msg.newCG2 = [];
for (i=0; i < msg.newCG.length; i++) {
var groupTotal = 0;
for (j=0; j < msg.campaignGroup.length; j++) {
if (msg.campaignGroup[j].col10 === msg.newCG[i]){
groupTotal = groupTotal + msg.campaignGroup[j].col11
}
}
msg.newCG2.push(groupTotal)
}
Rather than looping 1.2M times, it would be more efficient to use a single-pass over the 4000 campaign groups, grouping by id to create an array of totals for all ids -- I like using the reduce() function for this:
var cgMap = msg.campaignGroups.reduce(function(arr, grp) {
var grpid = grp.col10;
var count = grp.col11;
var total = arr[grpid] || 0;
arr[grpid] = total + count;
},
[]);
I know, the reduce(...) function is not the easiest to grok, but it takes the second arg (the empty array) and passes it, along with each campaign group object in turn, to that inline function. The result should be a simple array of group totals (from col11), indexed by the group id (from col10).
Now, it's just a matter of returning the totals for those 300 ids found in msg.newCG -- and this map() function does that for us:
var cgOut = msg.newCG.map(function(gid) {
return cgMap[gid]; // lookup the total by group id
}
);
I've made some assumptions here, like the group ids are not terribly large integers, and are rather closely spaced (not too sparse). From the original code, I was not able to determine the format of the data you are wanting to return in msg.newCG2. The final push() function would append 2 integers onto the array -- the output group id and the total for that group. Having pairs of group ids and totals interleaved in a flat array is not a very useful data structure. Perhaps you meant to place the total value into an array, indexed by the group id? If so, you could re-write that line as:
msg.newCG2[msg.newCG[i]] = msg.groupTotals;

array undefined after pushing to object

I'm pushing an object into an array of objects. One of the properties of the object (which is an array) is showing up as undefined, after the object is pushed to the array. If you look at my code below, where it says, "WHY IS THIS SHOWING IN CONSOLE?" - it doesn't make sense because if the array is undefined, it should have been undefined in both of the previous two checks (where it says "THIS IS NOT SHOWING IN CONSOLE."). This is driving me crazy. SOS. Please help.
var pathCopy = unit.path; // THIS IS AN ARRAY AND IT IS NOT EMPY
var directionCopy = unit.direction;
if (pathCopy.length < 1) {
console.log('THIS IS NOT SHOWING IN CONSOLE');
}
var object = {
'type': 'move',
'time': gameTime,
'name': unit.name,
'vector': pathCopy,
'direction': directionCopy
};
currentGameEvents.push(object);
if (object.vector.length < 1) {
console.log('THIS IS NOT SHOWING IN CONSOLE');
}
for (var i = 0; i < currentGameEvents.length; i++) {
if (currentGameEvents[i].vector.length < 1) {
console.log('WHY IS THIS SHOWING IN CONSOLE??');
}
}
I’m not sure how or why, but your example works, if I insert the undefined variables (i.e. I replaced unit.path with [345,34623,52] or something that is an array literal).
The only difference to that is, that you actually assign the reference to that array, instead of declaring a new array.
var pathCopy = unit.path; // pathCopy and unit.path are the SAME array
var pathCopy = [345,346,345]; // pathCopy is a completely NEW array
To fix this, try something like
var pathCopy = unit.path.slice(); // pathCopy is a completely NEW array that contains the same values as unit.path
whenever you want to copy an array; add .slice() at the end!
Again, this is deducing from the only difference between your code and what I tried, and this is probably the reason, why your code doesn’t work.
Another assumption is that currentGameEvents is an Array. Your code wouldn’t work at all, otherwise. I’m mentioning this because the title states that you were trying to push to an object, which isn’t possible.

js - How to change one key's value based on another key's value in array of objects

I have an array of objects like this:
var chartData = [{count: 0, idTag: "24"}
{count: 0, idTag: "25"}
{count: 0, idTag: "26"}]
My code does some work and generates a number (totalValue). If totalValue matches the idTag, its count gets increased by one:
for (i=0; i<timesFlipped; i++) {
totalValue = runTrial();
for (j=0; j<chartData.length; j++) {
if (chartData[j].idTag === totalValue) {
chartData[j].count += 1;
break;
}
}
}
This is a simple example, but chartData could hold a few dozen objects, and timesFlipped can be hundreds of thousands of iterations.
Is this the best way to change the count value based on matching the idTag value? Is there a way to just find the index of the array with the correct idTag value without iterating over the whole array of objects?
Here's the page I'm working on if you want more context: http://blue.butler.edu/~aazman/coupling/
Update:
Thanks for all the suggestions. I kinda formed my own solution based on what I saw. A bit more background about the project which led me to my solution:
The length of chartData and the min idTag value are calculated based on user inputs when they click submit
Once min and length are calculated (thus the max idTag is known), chartData is iteratively initialized:
var min = 23-5*numNickel-numPenny; //numNickel & numPenny are the user inputs
var length = 5*(2*numNickel+1)+2*numPenny;
var tempTag = min;
for (j=0;j<length;j++) {
chartData[j] = {count: 0, idTag = tempTag};
tempTag++;
}
For reasons particular to the rules of the game, the idTag values (however many there are) are symmetrically centered around idTag:25 (e.g. idTags = {24, 25, 26} or idTags = {20,21,22,23,24,25,26,27,28,29,30}. No matter the length of chartData, the length will always be odd and idTag:25 will always be the [(chartData.length-1)/2]'th index.
Thus, if the idTags for chartData are {20,21,22,23,24,25,26,27,28,29,30} and if runTrial() gives totalValue = 22, then idTag:22 will always be at index (totalValue - min).
So I changed the initialization of chartData and the way the count gets increased to:
for (j=0; j<length; j++) {
chartData[j] = {count: 0}; //based on what I'm doing with chartData next, I need each index to still be an object
}
for (i=0; i<timesFlipped; i++) {
totalValue = runTrial();
var index = totalValue - min;
chartData[index].count++;
}
So thanks all for your help :)
I would suggest using client side database storage mechanisms, and let the queries do the counting for you.
This makes it more overseeable and reduces menory warnings from various virusscanners, plus you dont need to refetch data everytime.
Another approach would be to use a server side database and retrieve the values you want via json.
another aproach toprevent browser freeze is to let your loop run in a webworker so its a thread and doesnt lock the browser.
You could create another object which points to the objects in the chartData array, like so:
var idToChart = {};
for (var i = 0; i < chartData.length; i++) {
var currChart = chartData[i];
idToChart[currChart.idTag] = currChart;
}
and then use
var chart = idToChart[totalValue];
chart.count++;
Accessing the object's property should be faster than looping through the array each time.
If, as #zerkms pointed out, your array is sorted by idtag, you wouldn't even need to create another object and could access the array directly. Ideally, chartData would start in the idToChart or sorted array format.
I would rather maintain a intermediate hash with a "idTag" value as a key and its count as value, perform the operation with for loop, then generate charData from the intermediate hash once the for loop completes.
If you're free to change the structure of chartData, why not just make it a hash instead of an array?
var chartData = {24: 0,
25: 0,
26: 0};
And then the loop becomes
for (i=0; i<timesFlipped; i++) {
chartData[runTrial()]++;
}

Can I select 2nd element of a 2 dimensional array by value of the first element in Javascript?

I have a JSON response like this:
var errorLog = "[[\"comp\",\"Please add company name!\"],
[\"zip\",\"Please add zip code!\"],
...
Which I'm deserializing like this:
var log = jQuery.parseJSON(errorLog);
Now I can access elements like this:
log[1][1] > "Please add company name"
Question:
If I have the first value comp, is there a way to directly get the 2nd value by doing:
log[comp][1]
without looping through the whole array.
Thanks for help!
No. Unless the 'value' of the first array (maybe I should say, the first dimension, or the first row), is also it's key. That is, unless it is something like this:
log = {
'comp': 'Please add a company name'
.
.
.
}
Now, log['comp'] or log.comp is legal.
There are two was to do this, but neither avoids a loop. The first is to loop through the array each time you access the items:
var val = '';
for (var i = 0; i < errorLog.length; i++) {
if (errorLog[i][0] === "comp") {
val = errorLog[i][1];
break;
}
}
The other would be to work your array into an object and access it with object notation.
var errors = {};
for (var i = 0; i < errorLog.length; i++) {
errors[errorLog[i][0]] = errorLog[i][1];
}
You could then access the relevant value with errors.comp.
If you're only looking once, the first option is probably better. If you may look more than once, it's probably best to use the second system since (a) you only need to do the loop once, which is more efficient, (b) you don't repeat yourself with the looping code, (c) it's immediately obvious what you're trying to do.
No matter what you are going to loop through the array somehow even it is obscured for you a bit by tools like jQuery.
You could create an object from the array as has been suggested like this:
var objLookup = function(arr, search) {
var o = {}, i, l, first, second;
for (i=0, l=arr.length; i<l; i++) {
first = arr[i][0]; // These variables are for convenience and readability.
second = arr[i][1]; // The function could be rewritten without them.
o[first] = second;
}
return o[search];
}
But the faster solution would be to just loop through the array and return the value as soon as it is found:
var indexLookup = function(arr, search){
var index = -1, i, l;
for (i = 0, l = arr.length; i<l; i++) {
if (arr[i][0] === search) return arr[i][1];
}
return undefined;
}
You could then just use these functions like this in your code so that you don't have to have the looping in the middle of all your code:
var log = [
["comp","Please add company name!"],
["zip","Please add zip code!"]
];
objLookup(log, "zip"); // Please add zip code!
indexLookup(log, "comp"); // Please add company name!
Here is a jsfiddle that shows these in use.
Have you looked at jQuery's grep or inArray method?
See this discussion
Are there any jquery features to query multi-dimensional arrays in a similar fashion to the DOM?

Javascript: push array onto array with for loop

Please explain this to me. I'm trying to create an array of arrays with a for loop. When it didn't work, I tried simplifying the code to understand what Javascript is doing, but the simple code doesn't make sense either.
function test(){
var sub_array = [];
var super_array =[];
for (var i=1;i<=3;i++){
sub_array.push(i);
super_array.push(sub_array);
}
alert(super_array);
}
I expect to see [1; 1,2; 1,2,3].
Instead I get [1,2,3; 1,2,3; 1,2,3].
I get the same phenomenon if I loop 0-2 and assign by index.
You're always pushing a reference to the same array into your super-array.
To solve that problem, you can use slice() to clone the sub-array before pushing it:
function test() {
var sub_array = [];
var super_array = [];
for (var i = 1; i <= 3; i++) {
sub_array.push(i);
super_array.push(sub_array.slice(0));
}
alert(super_array);
}
EDIT: As Dan D. rightfully points out below, you can also call concat() without arguments instead of slice(0). It's faster according to this article (I did not measure it myself):
for (var i = 1; i <= 3; i++) {
sub_array.push(i);
super_array.push(sub_array.concat());
}
When you push "sub_array", you're not pushing a copy of it. You end up with the same array three times in "super_array". (I should say that you're pushing a reference to the same array three times.)
You could do this:
// ...
super_array.push(sub_array.slice(0));
to make a copy.
well. You have to understand, that Array, Objects, Functions, etc. are references in javascript (only Numbers(Int,Floats,etc) and Strings are passed "by-value", which means, that the value is copied/duplicated)!
if you have an var a=[];, und say var b=a and add b.push("bla"), then alerting a, will show you the "bla" entry, even though you added it to b.
In other words; a and b is to javascript like a note on the frige from mom saying "the sandwhich on the left is for you." And then you know, that to take the left one and not just any random sandwich from the fridge. She also could have written another note (variable b) on your house' door, so that you knew where to go and look for the sandwich if you are in a hurry.
If she would have stuck a sandwich to the door.. well, that would be ackward. And JS thinks the same about it :)
so the solution to your problem is as fallows;
function test(){
var super_array =[];
for (var i=1;i<=3;i++){
var subarray=[];
for (var u=1;u<=4-i;u++){
sub_array.push(u);
super_array.push(subarray);
}
}
alert(super_array);
}
by redefining the subarray, you create a new reference. So that the variable b (the second note on the hous' door) now points in the direction of a different sandwich - maybe dad's sandwich.
I hope I could help you understand this.
Note that you are pushing the same array into super_array for each iteration in the for-loop. Try instead the following:
function test(){
var sub_array = [];
var super_array =[];
for (var i=1;i<=3;i++){
sub_array = sub_array.slice(0,sub_array.length);
sub_array.push(i);
super_array.push(sub_array);
}
alert(super_array);
}
It is same sub_array that you are adding to the super_array. So why it has to be different.
You are not creating a new array and pushing into a super_array.
sub_array is stored as a reference in super_array this means that when you change sub_array the change is reflected inside super_array

Categories