for loop iterates only once - javascript

I have a clients array of objects, so i use for loop to iterates over it
I have clients.lenght = 2, but it's iterates only 1. And i don't have variable scope problem here. Just don't get it why it happens.
Code
socket.on('idleDisconnectAllClient', function(receivedData) {
LOG("idleDisconnectAllClient");
var clientIndex = findMainIndexByDataBaseID(receivedData.dataBaseID);
if (clientIndex != -1) {
console.log('clients lenght', clients[clientIndex].data.length);
for (var i = 0; i < clients[clientIndex].data.length; i++) {
var client = clients[clientIndex].data[i];
client.disconnect();
console.log('client ' + i);
}
}
});
Console
2016-03-29 04:13:01 - idleDisconnectAllClient
clients lenght 2
client 0

jpaljasma - https://stackoverflow.com/users/2079695/jpaljasma
helps me to understand the problem.
When i use - client.disconnect() it's just delete current client from clients array. So thats why my loop carried out only 1 time.

you could iterate via the ES5 forEach construct:
clients[clientIndex].data.forEach(client => client.disconnect())
(disclaimer, this uses fat arrows from ES6, might not work on older node versions)

Related

Code is getting stuck somewhere in a succession of for-loops and I'm not sure why

EDIT - I changed the code to correctly declare variables below but nothing seems to have changed
I've written code using a for-loop that has to satisfy a number of criteria before executing what's within it. The problem is that, somewhere along the way, the code is getting stuck inside one of the loops, causing the computer to crash.
I've tried breaking the loop but this doesn't seem to help.
function compareKeypoints(varifiedKeypoints) {
outer_loop: for (i = 0; i < varifiedKeypoints.length; i++) {
let initialKeypoint = varifiedKeypoints[i];
for (j = 0; j < varifiedKeypoints.length; j++) {
let comparisonKeypoint = varifiedKeypoints[j];
if (initialKeypoint.part != comparisonKeypoint.part) {
if (Math.abs(comparisonKeypoint.position.x - initialKeypoint.position.x) <= 20
&& Math.abs(comparisonKeypoint.position.y - initialKeypoint.position.y) <= 20) {
if (keypointsCompatible(initialKeypoint.part, comparisonKeypoint.part)) {
console.log("Activating part: " + initialKeypoint.part);
console.log("Activated part: " + comparisonKeypoint.part);
let keypointPair = {
point_1: initialKeypoint.part,
point_2: comparisonKeypoint.part
}
console.log("Pushing parts!");
activeParts.push(keypointPair);
console.log("breaking loop!");
break outer_loop;
console.log("Loop NOT broken!!");
}
}
}
}
}
if (activeParts.length > 0) {
console.log(activeParts);
}
}
function keypointsCompatible(keypoint_1, keypoint_2) {
var outcome = true;
if (activeParts.length > 0) {
compatibility_loop: for (i = 0; i < activeParts.length; i++) {
if (Object.values(activeParts[i]).includes(keypoint_1) && Object.values(activeParts[i]).includes(keypoint_2)) {
console.log(keypoint_1 + " and " + keypoint_2 + " are not compatible because they already exist as " + activeParts[i].point_1 + " and " + activeParts[i].point_2 + " respectively");
outcome = false;
break compatibility_loop;
console.log("Compatibility NOT broken!!");
}
}
}
console.log("Compatibility outcome is " + outcome);
return outcome;
}
The code is suppose to take two values in the same array and compare them. If a number of conditions are met, including if they're a certain distance apart from one another, they will be pushed into a secondary array. If the values already appear in the secondary array, which the keypointCompatible function is suppose to determine, the loop should either continue looking for other candidates or stop before being called again. For some reason, however, the code is getting stuck within the keypointCompatible function when it detects that the values have already appeared in the secondary array and the console will repeatedly print "Compatibility is false" until the browser crashes.
Working Solution
Use let or const instead of var or nothing. Your issue may be related to closures and variables reused between loops. Make sure you use let or const in your loops too. for (let i=0).
When you use let or const, the runtime will create a new instance every time the block or loop iterates. However, using var will reuse the internal allocation.
So what happens with the standard var is the multiple closures or loops each use the same instance of the variable.
Unless you want the var behavior, always use let or const.
Another Solution
Put a newline after the label compatibility_loop
Still Another Solution
The first function is pushing into activeParts. The second function is looping activeParts. This can go on forever, or longer than expected. Pushing into the array could possibly make the loop limit never reached.
Put a log on the length of activeParts in the second function to see if it is growing out of control.
Your code should be OK if varifiedKeypoints.length has reasonable value. And all internal variables are declared properly!
You have two loops (this inner can start at j=i+1 to save time and multiple calculations) with few conditions inside.
function compareKeypoints(varifiedKeypoints) {
outer_loop: for (let i = 0; i < varifiedKeypoints.length; i++) {
let initialKeypoint = varifiedKeypoints[i];
for (let j = i+1; j < varifiedKeypoints.length; j++) {
let comparisonKeypoint = varifiedKeypoints[j];

RangError: too many arguments provided for a function call

I got a nice solution to get HTML Comments from the HTML Node Tree
var findComments = function(el) {
var arr = [];
for (var i = 0; i < el.childNodes.length; i++) {
var node = el.childNodes[i];
if (node.nodeType === 8) {
arr.push(node);
} else {
arr.push.apply(arr, findComments(node));
}
}
return arr;
};
var commentNodes = findComments(document);
// whatever you were going to do with the comment...
console.log(commentNodes[0].nodeValue);
from this thread.
Everything I did was adding this small loop to print out all the nodes.
var arr = [];
var findComments = function(el) {
for (var i = 0; i < el.childNodes.length; i++) {
var node = el.childNodes[i];
if (node.nodeType === 8) {
arr.push(node);
} else {
arr.push.apply(arr, findComments(node));
}
}
return arr;
};
var commentNodes = findComments(document);
//I added this
for (var counter = arr.length; counter > 0; counter--) {
console.log(commentNodes[counter].nodeValue);
}
I keep getting this Error Message:
RangeError: too many arguments provided for a function call debugger
eval code:9:13
EDIT: i had a typo while pasting changed the code from i-- to counter--
see this comment in MDN docs about the use of apply to merge arrays:
Do not use this method if the second array (moreVegs in the example) is very large, because the maximum number of parameters that one function can take is limited in practice. See apply() for more details.
the other note from apply page:
But beware: in using apply this way, you run the risk of exceeding the JavaScript engine's argument length limit. The consequences of applying a function with too many arguments (think more than tens of thousands of arguments) vary across engines (JavaScriptCore has hard-coded argument limit of 65536), because the limit (indeed even the nature of any excessively-large-stack behavior) is unspecified. Some engines will throw an exception. More perniciously, others will arbitrarily limit the number of arguments actually passed to the applied function. To illustrate this latter case: if such an engine had a limit of four arguments (actual limits are of course significantly higher), it would be as if the arguments 5, 6, 2, 3 had been passed to apply in the examples above, rather than the full array.
As the array start from index of 0, actually the last item in the array is arr.length - 1.
you can fix it by:
for (var counter = arr.length - 1; counter >= 0; counter--)
Notice I've added arr.length -1 and counter >= 0 as zero is the first index of the array.
Adding the for loop is not the only thing you changed (and see the other answer about fixing that loop too). You also moved the declaration of arr from inside the function to outside, making arr relatively global.
Because of that, each recursive call to findComments() works on the same array, and the .apply() call pushes the entire contents back onto the end of the array every time. After a while, its length exceeds the limit of the runtime.
The original function posted at the top of your question has arr declared inside the function. Each recursive call therefore has its own local array to work with. In a document with a lot of comment nodes, it could still get that Range Error however.

how to work with a large array in javascript [duplicate]

This question already has answers here:
Best way to iterate over an array without blocking the UI
(4 answers)
Closed 6 years ago.
In my application I have a very big array (arround 60k records). Using a for loop I am doing some operations on it as shown below.
var allPoints = [];
for (var i = 0, cLength = this._clusterData.length; i < cLength; i+=1) {
if (allPoints.indexOf(this._clusterData[i].attributes.PropertyAddress) == -1) {
allPoints.push(this._clusterData[i].attributes.PropertyAddress);
this._DistClusterData.push(this._clusterData[i])
}
}
When I run this loop the browser hangs as it is very big & in Firefox is shows popup saying "A script on this page may be busy, or it may have stopped responding. You can stop the script now, or you can continue to see if the script will complete". What can I do so that browser do not hang?
You need to return control back to the browser in order to keep it responsive. That means you need to use setTimeout to end your current processing and schedule it for resumption sometime later. E.g.:
function processData(i) {
var data = clusterData[i];
...
if (i < clusterData.length) {
setTimeout(processData, 0, i + 1);
}
}
processData(0);
This would be the simplest thing to do from where you currently are.
Alternatively, if it fits what you want to do, Web Workers would be a great solution, since they actually shunt the work into a separate thread.
Having said this, what you're currently doing is extremely inefficient. You push values into an array, and consequently keep checking the ever longer array over and over for the values it contains. You should be using object keys for the purpose of de-duplication instead:
var allPoints = {};
// for (...) ...
if (!allPoints[address]) { // you can even omit this entirely
allPoints[address] = true;
}
// later:
allPoints = allPoints.keys();
First of all, avoid the multiple this._clusterData[i] calls. Extract it to a variable like so:
var allPoints = [];
var current;
for (var i = 0, cLength = this._clusterData.length; i < cLength; i+=1) {
current = this._clusterData[i];
if (allPoints.indexOf(current.attributes.PropertyAddress) == -1) {
allPoints.push(current.attributes.PropertyAddress);
this._DistClusterData.push(current)
}
}
This should boost your performance quite a bit :-)
As others already pointed out, you can do this asynchronously, so the browser remains responsive.
It should be noted however that the indexOf operation you do can become very costly. It would be better if you would create a Map keyed by the PropertyAddress value. That will take care of the duplicates.
(function (clusterData, batchSize, done) {
var myMap = new Map();
var i = 0;
(function nextBatch() {
for (data of clusterData.slice(i, i+batchSize)) {
myMap.set(data.attributes.PropertyAddress, data);
}
i += batchSize;
if (i < clusterData.length) {
setTimeout(nextBatch, 0);
} else {
done(myMap);
}
})();
})(this._clusterData, 1000, function (result) {
// All done
this._DistClusterData = result;
// continue here with other stuff you want to do with it.
}.bind(this));
Try considering adding to the array asynchronously with a list, for a set of 1000 records at a time, or for what provides the best performance. This should free up your application while a set of items is added to a list.
Here is some additional information: async and await while adding elements to List<T>

getElementByID loop returning null

Chrome developer tools says that the value function doesn't work on a null value and points to the line in the for loop. Why isn't getElementByID fetching my values? (this is a refactor, getElement work perfect with the actual values typed in).
locationStops = ["start","end"];
var stopNum = locationStops.length;
var stopAddresses = [];
for(val in locationStops) {
stopAddresses.push(document.getElementById(val).value);
}
You could avoid the for loop, and the potential for bugs that you ran into with, by using map:
stopAddresses = locationStops . map(function(id) {
return document.getElementById(id).value;
});
Depending on your stylistic preferences, you might find the following more readable:
function get_value_from_id(id) {
return document.getElementById(id).value;
}
stopAddresses = locationStops . map(get_value_from_id);
If you want to use a loop, you could use the new for...of construct:
for (let val of locationStops) {
^^
stopAddresses.push(document.getElementById(val).value);
}
If you have an environment that supports ES7 array comprehensions:
[ for (id of locationStops) document.getElementById(id).value ]
If you want to stick with your for...in loop, then as other answers have pointed out, the loop variable is the index, not the value, so you have to access the ID with locationStops[i], but you are better off using a regular for loop.
Do not use for in for arrays.
Use a simple for loop instead.
var a = ["start", "end"];
for(var i = 0; i < a.length; ++i)
{
console.log(document.getElementById(a[i]).value);
}
You can use for-in also but, it is not recommended as it results in unexpected behaviour sometimes.
val refers to 0,1 etc. So there must be elements with ID's 0,1.
for(var val in a)
{
console.log(document.getElementById(a[val]).value);
}
Your code is not working because your for loop syntax is incorrect
Try This
var locationStops = ["start","end"];
var stopNum = locationStops.length;
var stopAddresses = [];
for(i = 0; i < locationStops.length; i++) {
stopAddresses.push(document.getElementById(locationStops[i]).value);
}
Alternatively, you could use Array.prototype.map.
var locationStops = ["start","end"];
var stopAddresses = locationStops.map(function(val) {
return document.getElementById(val).value;
});
Honestly, though, looping over a two element array is kind of silly and if it was my code I would even prefer to simply assign each address directly.
var stopAddresses = [document.getElementById("start").value, document.getElementById("end").value];

Javascript performance array of objects preassignment vs direct use

I have a doubt about how can be affected to speed the use of object data arrays, that is, use it directly or preasign them to simple vars.
I have an array of elements, for example 1000 elements.
Every array item is an object with 10 properties (for example).
And finally I use some of this properties to do 10 calculations.
So I have APPROACH1
var nn = myarray.lenght;
var a1,a2,a3,a4 ... a10;
var cal1,cal2,.. cal10
for (var x=0;x<nn;x++)
{ // assignment
a1=my_array[x].data1;
..
a10 =my_array[x].data10;
// calculations
cal1 = a1*a10 +a2*Math.abs(a3);
...
cal10 = (a8-a7)*4 +Math.sqrt(a9);
}
And APPROACH2
var nn = myarray.lenght;
for (var x=0;x<nn;x++)
{
// calculations
cal1 = my_array[x].data1*my_array[x].data10 +my_array[x].data2*Math.abs(my_array[x].data3);
...
cal10 = (my_array[x].data8-my_array[x].data7)*4 +Math.sqrt(my_array[x].data9);
}
Assign a1 ... a10 values from my_array and then make calculations is faster than make the calculations using my_array[x].properties; or the right is the opposite ?????
I dont know how works the 'js compiler' ....
The kind of short answer is: it depends on your javascript engine, there is no right and wrong here, only "this has worked in the past" and "this don't seem to speed thing up no more".
<tl;dr> If i would not run a jsperf test, i would go with "Cached example" 1 example down: </tl;dr>
A general rule of thumb is(read: was) that if you are going to use an element in an array more then once, it could be faster to cache it in a local variable, and if you were gonna use a property on an object more then once it should also be cached.
Example:
You have this code:
// Data generation (not discussed here)
function GetLotsOfItems() {
var ret = [];
for (var i = 0; i < 1000; i++) {
ret[i] = { calc1: i * 4, calc2: i * 10, calc3: i / 5 };
}
return ret;
}
// Your calculation loop
var myArray = GetLotsOfItems();
for (var i = 0; i < myArray.length; i++) {
var someResult = myArray[i].calc1 + myArray[i].calc2 + myArray[i].calc3;
}
Depending on your browser (read:this REALLY depends on your browser/its javascript engine) you could make this faster in a number of different ways.
You could for example cache the element being used in the calculation loop
Cached example:
// Your cached calculation loop
var myArray = GetLotsOfItems();
var element;
var arrayLen = myArray.length;
for (var i = 0; i < arrayLen ; i++) {
element = myArray[i];
var someResult = element.calc1 + element.calc2 + element.calc3;
}
You could also take this a step further and run it like this:
var myArray = GetLotsOfItems();
var element;
for (var i = myArray.length; i--;) { // Start at last element, travel backwards to the start
element = myArray[i];
var someResult = element.calc1 + element.calc2 + element.calc3;
}
What you do here is you start at the last element, then you use the condition block to see if i > 0, then AFTER that you lower it by one (allowing the loop to run with i==0 (while --i would run from 1000 -> 1), however in modern code this is usually slower because you will read an array backwards, and reading an array in the correct order usually allow for either run-time or compile-time optimization (which is automatic, mind you, so you don't need to do anything for this work), but depending on your javascript engine this might not be applicable, and the backwards going loop could be faster..
However this will, by my experience, run slower in chrome then the second "kinda-optimized" version (i have not tested this in jsperf, but in an CSP solver i wrote 2 years ago i ended caching array elements, but not properties, and i ran my loops from 0 to length.
You should (in most cases) write your code in a way that makes it easy to read and maintain, caching array elements is in my opinion as easy to read (if not easier) then non-cached elements, and they might be faster (they are, at least, not slower), and they are quicker to write if you use an IDE with autocomplete for javascript :P

Categories

Resources