Performance issue due to more for loop - javascript

Because of the more "for" loops causing performance issue. It's taking 15-20secs to load the page. The first three for loops are getting from three different JSON files.
Code:
$scope.loading = function(item, scode) {
item.calling = [];
for (var i = 0; i < $scope.planMapping.length; i++) {
for (var x = 0; x < $scope.callplanList.length; x++) {
for (var z = 0; z < $scope.AHOfferScode.length; z++) {
if (($scope.planMapping[i].PlanScode == $scope.callplanList[x].un_s_code) &&
($scope.callplanList[x].offer_type == "REC") &&
($scope.callplanList[x].s_code == $scope.AHOfferScode[z])
) {
//console.log($scope.devicesList);
for (var a = 0; a < $scope.callplanList[x].upfront_cost.length; a++) {
item.calling.push($scope.callplanList[x]);
}
}
}
}
}
Is there any replacement for "for" loop (filter or something) to improve the performance, something like:
euPlanDeviceScodes. PlanScode = CallPlan.un_s_code
availableHandsetOfferScodes = CallPlan.s_code
CallPlan.offer_type = “REC”

As commented before, array functions like filter are useful to improve readability but comes at a cost of performance. The difference is not much but still if you want performant code, for is the best option.
That said, you can improve the logic.
In your loop, you have a condition,
$scope.callplanList[x].offer_type == "REC"
This condition is not dependent on any looping variable and can be processed before looping
$scope.loading = function(item, scode) {
item.calling = [];
var recOfferList = $scope.callplanList.filter((plan) => plan.offer_type === 'REC');
for (var i = 0; i < $scope.planMapping.length; i++) {
for (var x = 0; x < recOfferList.length; x++) {
for (var z = 0; z < $scope.AHOfferScode.length; z++) {
if (($scope.planMapping[i].PlanScode == recOfferList[x].un_s_code) &&
(recOfferList.s_code == $scope.AHOfferScode[z])
) {
//console.log($scope.devicesList);
for (var a = 0; a < recOfferList[x].upfront_cost.length; a++) {
item.calling.push(recOfferList[x]);
}
}
}
}
}
Another place for optimization can be the innermost loop:
for (var a = 0; a < $scope.callplanList[x].upfront_cost.length; a++) {
item.calling.push($scope.callplanList[x]);
}
Above code is not using a anywhere in body. This can be replaced with
item.calling = item.calling.concat(
new Array.fill($scope.callplanList[x].upfront_cost.length)
.fill($scope.callplanList[x])
)
Or if you can use ES6 features, may be Array.from
item.calling = item.calling.concat(
Array.from({
length: $scope.callplanList[x].upfront_cost.length
}, () => $scope.callplanList[x])
)

Use find() instead of using loop to check the if condition. It may restrict a loop
like,
var array1= [1,2,3];
var array2= [3,4,5];
for(var i=0; i < array2.length;i++)
{
var matchedData = array1.find(function(x){ return x===array2[i]});
if(matchedData)
{
console.log(matchedData);
}
}
Now you can reduce one loop. as same logic do it.

Related

Trying to eliminate children 3-7 from an element, I am looping through the array to eliminate them but it wont work

So first main has only one child which is why I selected the child blades as var blades = main.children[0]; then I am trying to loop through and just eliminate the children within blades from [3] through [7] but for some reason when doing this on the console it won't work.
function deleteBlades() {
var main = document.getElementById("id-9a1e3f8f48548d4c2c422b1f92cb749a");
var blades = main.children[0];
for (var i = 0; i < blades.length; i++) {
if( i >= 3 || i <= 7)
{
blades.children[i].remove();
}
}
}
deleteBlades();
Any ideas on what am I doing wrong?
in your for loop you are reading blades.length when it is a HTMLElement not array, use blades.children.length instead
function deleteBlades() {
var main = document.getElementById("id-9a1e3f8f48548d4c2c422b1f92cb749a");
var blades = main.children[0];
for (var i = 0; i < blades.children.length; i++) {
if( i >= 3 || i <= 7)
{
blades.children[i].remove();
}
}
}
deleteBlades();
Looks like you got a typo in your loop.
I think what you meant to do is:
for (var i = 0; i < blades.children.length; i++) {

Detecting object collision with spatial partitioning in JavaScript

I'm trying to create a system of many objects that preform an action when they collide with each other, I'm using the P5.min.js library.
I've set up an array for the grid and an array for the objects, but I can't figure out the right way to go through each grid cell and check only the objects inside that cell before moving on to the next cell.
Here's what I've got so far
let molecules = [];
const numOfMolecules = 100;
let collisions = 0;
let check = 0;
let maxR = 10; //max molecule radius
let minR = 2; //min molecule radius
let numOfCol = 5;
let numOfRow = 5;
let CellW = 600/numOfCol; //gridWidth
let CellH = 600/numOfRow; //gridHeight
let remain = numOfMolecules;
let gridArray = [];
function setup() {
createCanvas(600, 600);
background(127);
for (let i = 0; i < numOfMolecules; i++) {
molecules.push(new Molecule());
}
}
function draw() {
background(127);
molecules.forEach(molecule => {
molecule.render();
molecule.checkEdges();
molecule.step();
});
drawGrid();
splitIntoGrid();
collision();
displayFR();
}
function drawGrid() {
for (i = 0; i < numOfRow+1; i++){
for (j = 0; j < numOfCol+1; j++){
noFill();
stroke(0);
rect(CellW*(j-1), CellH*(i-1), CellW, CellH);
}
}
}
function splitIntoGrid(){
for (let i = 0; i < numOfRow; i++){
for (let j = 0; j < numOfCol; j++){
tempArray = [];
molecules.forEach(molecule => {
if (molecule.position.x > (CellW*j) &&
molecule.position.x < (CellW*(j+1)) &&
molecule.position.y > (CellH*i) &&
molecule.position.y < (CellH*(i+1))) {
tempArray.push(molecule.id);
}
});
}
}
}
How I'm checking collision between all objects:
function collision() {
for (let i=0; i < molecules.length; i++){
for (let j=0; j < molecules.length; j++){
let diff = p5.Vector.sub(molecules[j].position, molecules[i].position);
check++;
if (i != j && diff.mag() <= molecules[j].radius + molecules[i].radius){
collisions++;
molecules[j].changeColor();
}
}
}
}
As far as I can see, I need to put these for loops inside another one going through each cell in the grid, but I don't know how to limit the search to which ever tempArray(s) the object is in
If this makes any sense, this is what I'm trying to do
function collision() {
for (let k = 0; k < gridArray.length; k++){
for (let i=0; i < gridArray.tempArray.length; i++){
for (let j=0; j < gridArray.tempArray.length; j++){
let diff = p5.Vector.sub(gridArray.tempArray[j].position, gridArray.tempArray.position);
check++;
if (i != j && diff.mag() <= gridArray.tempArray[j].radius + gridArray.tempArray[i].radius){
collisions++;
gridArray.tempArray[j].changeColor();
gridArray.tempArray[i].changeColor();
}
}
}
}
}
The grid cell is represented by an array of array gridArray. You need to have a collection of molecules for each grid cell. My recommendation would be to use Sets instead of an Array since the order is irrelevant. The idea is to be able to access the set of molecules on a given grid cell (i,j) with the syntax:
gridArray[i][j]
The following code will create an array of numOfRow arrays:
const numOfRow = 5;
const gridArray = (new Array(numOfRow)).fill([]);
gridArray with look like this:
[ [], [], [], [], [] ]
Inside splitIntoGrid you are checking which molecules are in which grid cells. This is good. However, for each grid cell, you are overwriting the global variable tempArray. Therefore, at the end of the function's execution, tempArray will only hold the molecules of the last grid cell, which isn't what you want. For a given grid cell, we will add the right molecules to a Set associated with this grid cell.
The Set data structure has an #add method which appends a new element to the set:
function splitIntoGrid() {
for (let i = 0; i < numOfRow; i++) {
for (let j = 0; j < numOfCol; j++) {
gridArray[i][j] = new Set();
molecules.forEach(molecule => {
if (molecule.position.x > (CellW*j)
&& molecule.position.x < (CellW*(j+1))
&& molecule.position.y > (CellH*i)
&& molecule.position.y < (CellH*(i+1))) {
gridArray[i][j].add(molecule);
}
});
}
}
}
Now you're ready to check for collisions on each grid cells. We will have a total of four loops inside one another. Two to navigate through the grid and two to compare the molecules that are contained inside each grid cell:
function collision() {
for (let i = 0; i < numOfRow; i++) {
for (let j = 0; j < numOfCol; j++) {
gridArray[i][j].forEach(moleculeA => {
gridArray[i][j].forEach(moleculeB => {
const diff = p5.Vector.sub(moleculeA.position, moleculeB.position);
if (moleculeA != moleculeB && diff.mag() <= moleculeA.radius + moleculeB.radius) {
collisions++;
moleculeA.changeColor();
moleculeB.changeColor();
}
});
});
}
}
}
In the above code, #forEach comes in handy.

Javascript error detecting item in array

I am working on a minesweeper game in javascript. The mechanism that is causing me trouble is the for loop inside the Mine object that sets the isBomb variable to true or false.
var board = [];
var bombs = [];
var mines;
function findNeighbors(x,y) {
return 'work in progress'
}
function setup() {
// create bombs
for (var i = 0; i < 45; i++) {
var position = [floor(random(0,15)),floor(random(0,15))];
if (!bombs.includes(position)) {
bombs[i] = position;
}
}
// create board
for (var y = 0; y < 15; y++) {
board[y] = new Array();
for (var x = 0; x < 15; x++) {
board[y][x] = new Mine(y,x);
}
}
}
console.log(board);
console.log(bombs);
function Mine(x,y) {
this.x = x;
this.y = y;
this.neighbors = findNeighbors(this.x,this.y);
for (var iter = 0; iter < 45; iter++) {
if (bombs[iter] == [this.x,this.y]) {
this.isBomb = true;
}
else {
this.isBomb = false;
}
}
this.show = function() {
return 'show'
}
this.setValue = function(value) {
this.value = value;
return value;
}
}
When I type bombs[44] in the console for example, it returns something like [5,11] yet when I check if bombs[44] = [5,11] it will always return false. Is there a specific way I have to denote the [5,11] array for it to be recognized?
This is because you cannot compare two arrays in javascript. What you can do is using join() and then compare as strings in single step,
bombs[44].join(",") == [5,11].join(",")
Or you can compare the contents of the array individually
Try changing the conditional expression in your for-loop at the top for (var i = 0; i < 45; i++) to for (var i = 0; i < arr.length; i++)
this will make the loop , go over the full array length with less room for error. for loops can be , error prone and tedious.

setting a variable to each element of an array

i have function:
function getFieldNames(arrayOfRecords) {
var theStuff;
for (var i = 0; i = arrayOfRecords.length - 1; i++){
theStuff = arrayOfRecords[i];
theList = theStuff.split('" ');
for (var j = 0; j = theList.length - 1; j++) {
var v = theList[j].split('="');
fName1[i][j] = v[0];
}
}
return fName1;
}
the argument arrayOfRecords is an array, and i dont know how to setup to the 'theStuff' variable an array element? When I do like it is above, i get something stupid.
can anyone help me? :)
There may be other problems but the one that leaps out at me is your for loop header:
for (var i = 0; i = arrayOfRecords.length - 1; i++)
The second part should be a condition, which when evaluated to false will stop the loop from running. What you probably wanted was:
for (var i = 0; i < arrayOfRecords.length; i++)
So when i is not less than arrayOfRecords.length, the loop will stop. Alternatively (to keep the - 1, but I tend to use the above version):
for (var i = 0; i <= arrayOfRecords.length - 1; i++)
The same goes for the nested loop.

What's the best way to break from nested loops in JavaScript? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 months ago.
Improve this question
What's the best way to break from nested loops in Javascript?
//Write the links to the page.
for (var x = 0; x < Args.length; x++)
{
for (var Heading in Navigation.Headings)
{
for (var Item in Navigation.Headings[Heading])
{
if (Args[x] == Navigation.Headings[Heading][Item].Name)
{
document.write("<a href=\""
+ Navigation.Headings[Heading][Item].URL + "\">"
+ Navigation.Headings[Heading][Item].Name + "</a> : ");
break; // <---HERE, I need to break out of two loops.
}
}
}
}
Just like Perl,
loop1:
for (var i in set1) {
loop2:
for (var j in set2) {
loop3:
for (var k in set3) {
break loop2; // breaks out of loop3 and loop2
}
}
}
as defined in EMCA-262 section 12.12. [MDN Docs]
Unlike C, these labels can only be used for continue and break, as Javascript does not have goto.
Wrap that up in a function and then just return.
I'm a little late to the party but the following is a language-agnostic approach which doesn't use GOTO/labels or function wrapping:
for (var x = Set1.length; x > 0; x--)
{
for (var y = Set2.length; y > 0; y--)
{
for (var z = Set3.length; z > 0; z--)
{
z = y = -1; // terminates second loop
// z = y = x = -1; // terminate first loop
}
}
}
On the upside it flows naturally which should please the non-GOTO crowd. On the downside, the inner loop needs to complete the current iteration before terminating so it might not be applicable in some scenarios.
I realize this is a really old topic, but since my standard approach is not here yet, I thought I post it for the future googlers.
var a, b, abort = false;
for (a = 0; a < 10 && !abort; a++) {
for (b = 0; b < 10 && !abort; b++) {
if (condition) {
doSomeThing();
abort = true;
}
}
}
Quite simple:
var a = [1, 2, 3];
var b = [4, 5, 6];
var breakCheck1 = false;
for (var i in a) {
for (var j in b) {
breakCheck1 = true;
break;
}
if (breakCheck1) break;
}
Here are five ways to break out of nested loops in JavaScript:
1) Set parent(s) loop to the end
for (i = 0; i < 5; i++)
{
for (j = 0; j < 5; j++)
{
if (j === 2)
{
i = 5;
break;
}
}
}
2) Use label
exit_loops:
for (i = 0; i < 5; i++)
{
for (j = 0; j < 5; j++)
{
if (j === 2)
break exit_loops;
}
}
3) Use variable
var exit_loops = false;
for (i = 0; i < 5; i++)
{
for (j = 0; j < 5; j++)
{
if (j === 2)
{
exit_loops = true;
break;
}
}
if (exit_loops)
break;
}
4) Use self executing function
(function()
{
for (i = 0; i < 5; i++)
{
for (j = 0; j < 5; j++)
{
if (j === 2)
return;
}
}
})();
5) Use regular function
function nested_loops()
{
for (i = 0; i < 5; i++)
{
for (j = 0; j < 5; j++)
{
if (j === 2)
return;
}
}
}
nested_loops();
var str = "";
for (var x = 0; x < 3; x++) {
(function() { // here's an anonymous function
for (var y = 0; y < 3; y++) {
for (var z = 0; z < 3; z++) {
// you have access to 'x' because of closures
str += "x=" + x + " y=" + y + " z=" + z + "<br />";
if (x == z && z == 2) {
return;
}
}
}
})(); // here, you execute your anonymous function
}
How's that? :)
How about using no breaks at all, no abort flags, and no extra condition checks. This version just blasts the loop variables (makes them Number.MAX_VALUE) when the condition is met and forces all the loops to terminate elegantly.
// No breaks needed
for (var i = 0; i < 10; i++) {
for (var j = 0; j < 10; j++) {
if (condition) {
console.log("condition met");
i = j = Number.MAX_VALUE; // Blast the loop variables
}
}
}
There was a similar-ish answer for decrementing-type nested loops, but this works for incrementing-type nested loops without needing to consider each loop's termination value for simple loops.
Another example:
// No breaks needed
for (var i = 0; i < 89; i++) {
for (var j = 0; j < 1002; j++) {
for (var k = 0; k < 16; k++) {
for (var l = 0; l < 2382; l++) {
if (condition) {
console.log("condition met");
i = j = k = l = Number.MAX_VALUE; // Blast the loop variables
}
}
}
}
}
If you use Coffeescript, there is a convenient "do" keyword that makes it easier to define and immediately execute an anonymous function:
do ->
for a in first_loop
for b in second_loop
if condition(...)
return
...so you can simply use "return" to get out of the loops.
I thought I'd show a functional-programming approach. You can break out of nested Array.prototype.some() and/or Array.prototype.every() functions, as in my solutions. An added benefit of this approach is that Object.keys() enumerates only an object's own enumerable properties, whereas "a for-in loop enumerates properties in the prototype chain as well".
Close to the OP's solution:
Args.forEach(function (arg) {
// This guard is not necessary,
// since writing an empty string to document would not change it.
if (!getAnchorTag(arg))
return;
document.write(getAnchorTag(arg));
});
function getAnchorTag (name) {
var res = '';
Object.keys(Navigation.Headings).some(function (Heading) {
return Object.keys(Navigation.Headings[Heading]).some(function (Item) {
if (name == Navigation.Headings[Heading][Item].Name) {
res = ("<a href=\""
+ Navigation.Headings[Heading][Item].URL + "\">"
+ Navigation.Headings[Heading][Item].Name + "</a> : ");
return true;
}
});
});
return res;
}
Solution that reduces iterating over the Headings/Items:
var remainingArgs = Args.slice(0);
Object.keys(Navigation.Headings).some(function (Heading) {
return Object.keys(Navigation.Headings[Heading]).some(function (Item) {
var i = remainingArgs.indexOf(Navigation.Headings[Heading][Item].Name);
if (i === -1)
return;
document.write("<a href=\""
+ Navigation.Headings[Heading][Item].URL + "\">"
+ Navigation.Headings[Heading][Item].Name + "</a> : ");
remainingArgs.splice(i, 1);
if (remainingArgs.length === 0)
return true;
}
});
});
How about pushing loops to their end limits
for(var a=0; a<data_a.length; a++){
for(var b=0; b<data_b.length; b++){
for(var c=0; c<data_c.length; c++){
for(var d=0; d<data_d.length; d++){
a = data_a.length;
b = data_b.length;
c = data_b.length;
d = data_d.length;
}
}
}
}
Already mentioned previously by swilliams, but with an example below (Javascript):
// Function wrapping inner for loop
function CriteriaMatch(record, criteria) {
for (var k in criteria) {
if (!(k in record))
return false;
if (record[k] != criteria[k])
return false;
}
return true;
}
// Outer for loop implementing continue if inner for loop returns false
var result = [];
for (var i = 0; i < _table.length; i++) {
var r = _table[i];
if (!CriteriaMatch(r[i], criteria))
continue;
result.add(r);
}
There are many excellent solutions above.
IMO, if your break conditions are exceptions,
you can use try-catch:
try{
for (var i in set1) {
for (var j in set2) {
for (var k in set3) {
throw error;
}
}
}
}catch (error) {
}
Hmmm hi to the 10 years old party ?
Why not put some condition in your for ?
var condition = true
for (var i = 0 ; i < Args.length && condition ; i++) {
for (var j = 0 ; j < Args[i].length && condition ; j++) {
if (Args[i].obj[j] == "[condition]") {
condition = false
}
}
}
Like this you stop when you want
In my case, using Typescript, we can use some() which go through the array and stop when condition is met
So my code become like this :
Args.some((listObj) => {
return listObj.some((obj) => {
return !(obj == "[condition]")
})
})
Like this, the loop stopped right after the condition is met
Reminder : This code run in TypeScript
Assign the values which are in comparison condition
function test(){
for(var i=0;i<10;i++)
{
for(var j=0;j<10;j++)
{
if(somecondition)
{
//code to Break out of both loops here
i=10;
j=10;
}
}
}
//Continue from here
}
An example with for .. of, close to the example further up which checks for the abort condition:
test()
function test() {
var arr = [1, 2, 3,]
var abort = false;
for (var elem of arr) {
console.log(1, elem)
for (var elem2 of arr) {
if (elem2 == 2) abort = true;
if (!abort) {
console.log(2, elem2)
}
}
}
}
Condition 1 - outer loop - will always run
The top voted and accepted answer also works for this kind of for loop.
Result: the inner loop will run once as expected
1 1
2 1
1 2
1 3
XXX.Validation = function() {
var ok = false;
loop:
do {
for (...) {
while (...) {
if (...) {
break loop; // Exist the outermost do-while loop
}
if (...) {
continue; // skips current iteration in the while loop
}
}
}
if (...) {
break loop;
}
if (...) {
break loop;
}
if (...) {
break loop;
}
if (...) {
break loop;
}
ok = true;
break;
} while(true);
CleanupAndCallbackBeforeReturning(ok);
return ok;
};
the best way is -
1) Sort the both array which are used in first and second loop.
2) if item matched then break the inner loop and hold the index value.
3) when start next iteration start inner loop with hold index value.

Categories