Slow down for-loop inside while-loop (Javascript) - javascript

So I'm trying to visualize a sorting algorithm in Vanilla JS and for this I need the actual sorting function to not sort it all in the fraction of a second but to wait e.g. 250ms after each iteration.
This is the Bubble Sort code (which does work):
function sortArray(){
let arr = inputArray; //arr is the array to get sorted.
let l = arr.length;
let swapped;
//---Bubble sort---
do {
swapped = false;
for (let i = 0; i < l-1; i++) {
if (arr[i] > arr[i+1]){
let temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
swapped = true;
}
}
}while (swapped);
//---Bubble sort---
display.innerText = JSON.stringify(arr); //Display the sorted Array to the user
}
Now I researched on how to slow down a loop in JS and tried a couple of different ways.
For example:
function sortArray(){
let arr = inputArray; //arr is the array to get sorted.
let l = arr.length;
let swapped;
//---Bubble sort---
do {
swapped = false;
for (let i = 0; i < l-1; i++) {
setTimeout(() =>{
if (arr[i] > arr[i+1]){
let temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
swapped = true;
n++;
}
arrayDisplay.innerText =JSON.stringify(arr);
},250 *i);
}
}while (swapped);
//---Bubble sort---
display.innerText = JSON.stringify(arr); //Display the sorted Array to the user
}
Here I tried to use the setTimeout function inside the for-loop which does theoretically work but it does only slow down and display each step for the for-loop but stops after each run instead of looping again (while(swapped))(obviously, but I don't know how to fix it). For each while loop I to press the button again.
I also tried wrapping the whole do-while construction inside the setTimeout and add an additional set Timeout inside the for-loop. That simply crashed my browser. I also tried a couple of other constellations but those also either crashed the browser or didn't sort at all.

setTimeout() creates an asynchronous process but for and while loops normally runs synchronously, so it doesn't work to do it like that. If your environment supports async-await syntax, you can use that to make for and while run asynchronously.
Annotate the function as async, promisify the timeout and then await it, like this:
async function sortArray(){
let arr = inputArray; //arr is the array to get sorted.
let l = arr.length;
let swapped;
//---Bubble sort---
do {
swapped = false;
for (let i = 0; i < l-1; i++) {
await new Promise(resolve => setTimeout(() =>{
if (arr[i] > arr[i+1]){
let temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
swapped = true;
n++;
}
arrayDisplay.innerText =JSON.stringify(arr);
resolve();
}, 250));
}
}while (swapped);
//---Bubble sort---
display.innerText = JSON.stringify(arr); //Display the sorted Array to the user
}
If your environment doesn't support async-await, you're going to need to use a more complex approach with callback functions instead of for and while loops.
Keep in mind that an async function will always return a Promise. It's not relevant in this case since your function creates side-effects and doesn't return anything.

Related

using for loop in quick sort in Js

i am writing quick sort algorthm, and when i testing my code, the result makes me confused.
RangeError: Maximum call stack size exceeded
at Array.push ()
let arr = [0,-1,1,0,1,-1,100,23,17,56,39,47,23,-34,-56];
export default function quickSort(array){
let len = array.length;
if(len <= 1) return array;
let piviot = array.pop();
let left = [], right = [];
for (let i = 0; i < len; i++) {
if(array[i] < piviot){
left.push(array[i]);
}
else{
right.push(array[i])
}
}
return quickSort(left).concat(piviot,quickSort(right));
}
when i change for loop to forEach, problem is disappeared, but i don't know why.
export default function quickSort(array){
let len = array.length;
if(len <= 1) return array;
let piviot = array.pop();
let left = [], right = [];
array.forEach(item => {
if(item < piviot){
left.push(item)
}
else{
right.push(item);
}
});
return quickSort(left).concat(piviot,quickSort(right));
}
Thanks.
This will work as piviot = array.pop(); changes array size which causes the issue
export default function quickSort(array){
let len = array.length;
if(len <= 1) return array;
let piviot = array.pop();
len = array.length;
let left = [], right = [];
for (let i = 0; i < len; i++) {
if(array[i] < piviot){
left.push(array[i]);
}
else{
right.push(array[i])
}
}
return quickSort(left).concat(piviot,quickSort(right));
}
I won't write the quicksort algorithm for you, but I will explain the behavior:
Why the first piece of code overflows stack
This is very common in recursive functions that never end. You are never reaching the magic
if(len <= 1) return array;
All your function calls end up on the call stack. There is a limit depending on the browser on how many can be called on the stack at the same time. Thus when you reach that limit with recursion (and not only) you overflow the stack.
Why for each works
It doesn't, it just won't overflow your stack. Why? Foreach is a callback. It won't run first and then go to the next line.
Using foreach, you are effectively calling the next recursion with empty arrays and they return automatically.

how to compare the running time between different functions

First, I want to thanks to Bails for his answer on question console.time shows different time running the same function. But it still confusing me that how can I compare running time of two different functions
Here's the code I have tried.
function fun1(arr) {
let a = 0
for(let i = 0;i < arr.length; i++) {
a += arr[i]
}
return a
}
function fun2(arr) {
let a = 0
arr.forEach((v) => {
a += v
})
return a
}
let array = []
for(let i = 0;i < 100; i++) {
array.push(Math.random())
}
console.time('fun1')
fun1(array)
console.timeEnd('fun1')
console.time('fun2')
fun2(array)
console.timeEnd('fun2')
We all know that forEach is faster than for. But when I run the code above, I got different results:

Why this reverse function isn't working?

Why isn't this working?
ps. I don't want to use any other variable to make it work, and i don't want to use built in functions, just asking why THIS is not working?
function reverse(arr){
for(var i =0; i< arr.length; i++){
arr.push(arr[arr.length-i]);
}
return arr;
}
There are a lot of flaws in your code.
When you start pushing arr.push(arr[arr.length-i]); the array length increases, thereby, you won't get a consistency in the data.
This goes inside an infinite loop, as every time, the arr is ahead of its length.
It is better to use another variable and reverse, or you can use the built-in reverse() function. There's nothing wrong in having another variable and add temporary contents in it.
Solutions:
Using a temporary array:
function reverse(arr) {
var final = [];
for (var i = arr.length - 1; i >= 0; i--) {
final.push(arr[i]);
}
return final;
}
Using built-in function (Array.prototype.reverse()):
function reverse(arr) {
return arr.reverse();
}
Using few temporary variables:
a = [5,4,3,2,1];
function reverse(arr) {
var i = 0, j = arr.length - 1;
for (i = 0; i < j; i++, j--) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
}
console.log(reverse(a));
You're going to run out of memory. What you're doing is adding what was initially the last element of that array infinitely to the end of your array. Every time that you call arr.push(...) you increase arr.length by one. Your for loop will never be able to finish because i will never be less than arr.length
I don't want to use any other variable to make it work, and i don't want to use built in functions
You cannot.
Use temporary array for result
function reverse(arr) {
var res = []
for (var i = arr.length - 1; i > -1; i--) {
res.push(arr[i]);
}
return res;
}

How To Remove All Odd Numbers In An Array Using Javascript?

Can someone debug this code? I cannot for the life of me find the (run-time) error:
function generate_fibonacci(n1, n2, max, out){
var n = n1+n2;
if(n<max){
out.push(n);
generate_fibonacci(n2, n, max, out);
}
}
function generate_fibonacci_sequence(max){
var out = [1];
generate_fibonacci(0, 1, max, out);
return out;
}
function remove_odd_numbers(arr){
for (var i = 0; i < arr.length; i++) {
if(!(arr[i]%2==0)){
arr.splice(i, 1);
}
}
return arr;
}
function sum(array){
var total = 0;
for (var i = 0; i < array.length; i++) {
total+=array[i];
}
return total;
}
var fib_sq = generate_fibonacci_sequence(4000000);
console.log("Before: " + fib_sq);
remove_odd_numbers(fib_sq);
console.log("After: " + fib_sq);
console.log("WTH?: " + remove_odd_numbers([1,2,3,4,5,6,7,8,9]));
Output:
Before: 1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578
After: 1,2,5,8,21,34,89,144,377,610,1597,2584,6765,10946,28657,46368,121393,196418,514229,832040,2178309,3524578
WTH?: 2,4,6,8
[Finished in 0.3s]
I'm going crazy or something. For some reason, all odd numbers are not being removed. But as you can see at the end, it works perfectly. I have no idea what is going on.
The problem in the original code is that when you remove the first 1 at index 0, the array gets shifted; now arr[i] is contains the second 1; but you just step over it.
You need to use while instead of if here, or copy to a separate list. This is an example for splicing:
function remove_odd_numbers1(arr){
for (var i = 0; i < arr.length; i++) {
// here
while (arr[i] % 2) {
arr.splice(i, 1);
}
}
return arr;
}
But it will be slow though. Better to create a new array:
function remove_odd_numbers2(arr){
var rv = [];
for (var i = 0; i < arr.length; i++) {
if (! (arr[i] % 2)) {
rv.push(arr[i]);
}
}
return rv;
}
Generally the best algorithm however is to use the same array, if the original is not needed, so that no extra memory is required (though on javascript this is of a bit dubious value):
function remove_odd_numbers3(arr){
var out = 0;
for (var i = 0; i < arr.length; i++) {
if (! (arr[i] % 2)) {
arr[out++] = arr[i];
}
}
arr.length = out;
return arr;
}
Notice however that unlike the splice algorithm, this runs in O(n) time.
Also, the Array.prototype.filter() is not bad, being a builtin. It also creates a new array and thus is comparable to the 2.
I'm not sure about this, however I doubt using splice is efficient compared to creating a new array.
function remove_odd_numbers(arr) {
var notOdd = [],
i = 0,
len = arr.length,
num;
for (; i < len; i++) {
!((num = arr[i]) % 2) && notOdd.push(num);
}
return notOdd;
}
EDIT: You should probably use the native filter function, as suggested by #Jack. I leave this answer as a reference.
Here is a really simple, fast way to do it. Using your data, it only took 48ms to complete. Hope this helps..
function noOdds(values){
return values.filter(function (num) {
return num % 2 === 0;
});
}
Because splice() modifies the array, your index will be off in the next iteration; you need to either decrease the loop variable, use a while loop like Antti proposed or iterate backwards like Crazy Train mentioned.
That said, the use of splice() is awkward to work with because it modifies the array in-place. This functionality can be easily accomplished using a filter function as well:
function remove_odd_numbers(arr)
{
return arr.filter(function(value) {
return value % 2 == 0;
});
}
This creates and returns a new array with only the even values.
Given the recency of this function, check the compatibility section how to handle browsers IE < 9. Many popular libraries, such as jQuery, underscore, etc. take care of this for you.
Update
Instead of filtering the array afterwards, it would be more memory efficient to only add the even values as you perform the recursion:
function generate_fibonacci(previous, current, max, callback)
{
var next = previous + current;
if (next < max) {
callback(next);
generate_fibonacci(current, next, max, callback);
}
}
function generate_fibonacci_sequence(max, callback)
{
callback(1);
callback(1);
generate_fibonacci(1, 1, max, callback);
}
var out = [];
generate_fibonacci_sequence(4000000, function(value) {
if (value % 2 == 0) {
out.push(value);
}
});
Instead of passing the out array, I'm passing a function to be called whenever a new sequence value is generated; the filtering is done inside that callback.
ES6 version from "Tabetha Moe" answer
function noOdds(arr) {
return arr.filter(value => value % 2 === 0);
}

checking if sum of array is greater than max number and vice versa javascript

function ArrayAdditionI(arr) {
var numbers = arr();
var arraySum = "";
for (var i = 0; i < numbers.length; i++) {
arraySum = arraySum + arr[i];
};
if (numbers.max() <= arraySum) {
arr = true
}
else if (numbers.max() > arraySum) {
arr = false;
}
return arr;
}
I need to find the numbers stored in an array called arr and check if they add up to or total the greatest number or whether they do not. If so, return true. If not, return false.
I am not sure I am calling the array correctly in the beginning.
Thanks
I actually wrote a library I use just for functions like this.
http://code.google.com/p/pseudosavant/downloads/detail?name=psMathStats.min.js
You would just do this:
var arr = [1,2,3,4,5,300];
if (arr.max() > arr.sum()){
// Max is greater than sum...
}
One warning though. This library prototypes the Array object which could mess up other scripting that uses for (var i in arr) on an Array, which you shouldn't ever do. I am actually almost done with v2 of the library with a number of new functions and it no longer prototypes the Array object.
You can just grab the .max() and .sum() methods from the code, and use them without the prototyping if you want though.
maxArray = function (arr) {
return Math.max.apply(Math, arr);
}
sumArray = function (arr) {
for (var i = 0, length = arr.length, sum = 0; i < length; sum += arr[i++]);
return sum;
}
You mean something like this?
function ArrayAdditionI(arr) {
for (var i = 0, sum=0; i < arr.length; i++) {
sum += arr[i];
}
return Math.max.apply( Math, arr ) <= sum;
}
function ArrayAdditionI(input) {
var arraySum, max;
arraySum = max = input[0];
for (var i = 1; i < input.length; i++) {
arraySum += input[i];
if(input[i] > max){
max = input[i];
}
};
return arraySum >= max;
}
If the numbers are positive, the answer is guaranteed - the sum is always greater than or equal to the max. If you need to calculate it, ddlshack's code looks good.
Looking at your code, there are a number of issues. First of all, arr() should error out. Arrays aren't functions, and trying to treat them as a function does nothing. Your array is already usable when it is passed in. Additionally, you want to initialize arraySum to 0, not "". The way you are doing it, the values in the array will be coerced into strings and concatenated together, which is not what you are looking for. Finally, arrays don't implement a max() method, but Math does, and functions/methods in javascript can be applied to an array in the manner shown by ddlshack and others.
There are some syntax errors: type missmatch, wrong assign and calls to method that doesn't exists. If I'm understanding what do you want to do, this is the correct code(if changing items order is not a problem):
function ArrayAdditionI(arr) {
var ret = false;
var arraySum = 0;
for (var i = 0; i < arr.length; i++) {
arraySum += arr[i];
}
if (arr.sort()[arr.length-1] <= arraySum) {
ret = true
}
return ret;
}

Categories

Resources