Bubble sort not swapping elements of array in Javascript - javascript

I am creating a simple program that should utilize the bubble sort algorithm to sort a list of numbers in ascending order.
Just for testing purposes I have added the line alert(unsortedNumbers);and as you can see if you run it, the numbers do not change order no matter how many passes the algorithm does.
The program seems to be stuck in an infinite loop, as 'Another pass' is printed to the console repeatedly. As instructed by this line console.log("Another pass");
As with the bubble sort algorithm, once it does not have to swap any terms on a certain pass, we know this is the sorted list, I have created the variable swapped, however it looks like this is always 1. I think this may be caused by the swapArrayElements() function not swapping the terms.
Why is the function not swapping the index of the terms within the array?
(Code does't seem to run properly on SO's code snippet tool, may have to copy into notepad document)
function main(){
var unsortedNumbers =[7,8,13,1,6,9,43,80]; //Declares unsorted numbers array
alert(unsortedNumbers);
var swapped = 0;
var len = unsortedNumbers.length;
function swapArrayElements(index_a, index_b) { //swaps swapArrayElements[i] with swapArrayElements[ii]
var temp = unsortedNumbers[index_a];
unsortedNumbers[index_a] = unsortedNumbers[index_b];
unsortedNumbers[index_b] = temp;
}
function finish(){
alert(unsortedNumbers);
}
function mainBody(){
for(var i =0;i<len;i++){
var ii =(i+1);
if (unsortedNumbers[i]>unsortedNumbers[ii]){
console.log("Swap elements");
swapArrayElements(i,ii);
swapped=1; // Variable 'swapped' used to check whether or not a swap has been made in each pass
}
if (ii = len){
if (swapped = 1){ // if a swap has been made, runs the main body again
console.log("Another pass");
alert(unsortedNumbers); //Added for debugging
swapped=0;
mainBody();
}else{
console.log("Finish");
finish();
}
}
}
}
mainBody();
}
<head>
</head>
<body onload="main()">
</body>

You have an error in your code:
if (ii = len) {
and also
if (swapped = 1){
it should be double equal

Invalid condition check causing infinite loop:
if (ii = len) & if (swapped = 1) should have == or === operator. This is causing infinity loop.
NOTE: Your code is not appropriate as per the best practices to avoid global variables. You should not use global variables and try
passing variables and returning them back after processing.
Refer this for avoiding globals.

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.
}

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.

javascript is there a way to increment a for loop with a variable

I have a function -- let's call it test(arg1,arg2), called from program1, which does a number of things and is working correctly. Within test there is a loop:
for(j=1;j<=top;j++) {
stuff happens based on j
}
I would like to call test(arg1,arg2) from a different program, say program2. Everything about test is the same for these two programs except the for loop. For program2 I need that loop to be
for(j=2;j<=top;j+=2) {
stuff happens based on j
}
Otherwise everything else is exactly the same.
The second argument, arg2 tells us whether the script was called from program1 or program2. But I can't figure out how to write a variable "for" statement. I tried an if statement based on arg2
var jstart = 1 or 2
var jincr = '++' or '+=2'
and then wrote the loop as
for(j=jstart;j<=top;j jincr) {
This did not work, although it is an approach that works in other languages.
Can someone suggest I way I can do this without writing an entirely separate script for the two cases?
As simple as that
jstart = 1 // or 2
jincr = 1 // or 2;
for(j=jstart;j<=top;j += jincr) {
The most reusable way would be to put your loop in a function that accepts increment as an argument:
function doStuff (inc) {
for(var j = inc; j <= top; j += inc) {
// stuff happens based on j
}
}
// Program 1
doStuff(1)
// Program 2
doStuff(2)

Vanilla JavaScript each function user controlled

So I've written a few each functions like jQuery has, and to be honest they are nice but I got to thinking, what if the user at hand does not want to start at 0, or wants to manipulate the iterator. I've tried many ways of doing this and it seems to either crash or cause an infinite loop. Here are some examples.
//a being an array of elements
_$(a).each(function(i){
console.log(i);
i++;
});
//backend js
each:function(fn){
var i = fn[0]; // equals the first argument in the callback
for(i=0;i<this.length;)
fn.call(this[i],i);
return this;
},
Next Example
//a being an array of elements
var inta = 6;
_$(a).each(function(i){
console.log(i);
},inta++);
//backend js
each:function(fn,iterator){
var len = this.length;
var ct = iterator;
while(ct < len)
fn.call(this[ct],ct);
return this;
},
There are a few more examples and they don't work either. So question is how do I manipulate the iterator inside the function like the first example above. If not possible that is fine just curious as to this curious possibility.
So what I've read above from map, filter, reduce, some, every, they just won't work with what I need since I am writing a small minimal library. So I need to know exactly what to iterate over, which in my case is this but only this[INT]. So I created an optional parameter after the function to add to the iterator by.
each:function(fn,add){
/*
First check if add is assigned
If it is not assign it to be 1 + 1 (i++)
If it is, make sure it is not 0 (causing an infinite loop)
And that will result in ( i + add)
*/
add = 0 !== add && add || 1;
for(var i=0;i<this.length;i = i + add)
fn.call(this[i],i);
return this;
}
So the use case would be.
var domElements = document.querySelectorAll('*'); //10 items per se
_$(domElements).each(function(i){
console.log(i);
},2);
//logs
//0 2 4 6 8
This is just a very basic iterator for now, and like I've said for a small convenient library not boogered by lots of "bull".

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