RangeError: Maximum call stack size exceeded with array.slice - javascript

Here I am trying to implement merge-sort with javascript but I am getting an error maximum call stack size exceeded at array.slice
function mergeSort(arr) {
if(arr.length < 1) return arr;
let len = arr.length
let middle = Math.floor(len/2)
let left = arr.slice(0, middle)
let right = arr.slice(middle)
return merge(mergeSort(left), mergeSort(right))
}

Recursion never stops.
When the function will be called with an array of length 1, this array will be split in one array with length 0 (there the recursion stops) and another array with length 1. The function will be called again recursively with the second array. This will produce the same situation again.
This means, you must handle the situation where the array has the length 1 correctly (as already stated by Raphael, sorry I didn't see this before). You can simply return an array of length 1 because there is no need to sort.

Related

Maximum call stack on non recursive function

Hello I'm running on a problem with this function that results in a maximum call stack exceeded error. This function is not recursive, so I don't really understand why it is exceeding the call stack.
I copied this function from some blog (maybe stackoveflow), It converts a Word Array to a Byte Array to use in pako.js.
It's used to inflate a zlib compressed string.
When the string is small It doesn't exceed the call stack, but with longer strings it exceeds it.
I've tried rewriting it with setTimeout, but it becomes very slow.
Do any of you have any suggestions?
Thanks.
const wordToByteArray = function (word, length) {
let ba = [];
let i;
let xFF = 0xFF;
if (length > 0)
ba.push(word >>> 24);
if (length > 1)
ba.push((word >>> 16) & xFF);
if (length > 2)
ba.push((word >>> 8) & xFF);
if (length > 3)
ba.push(word & xFF);
return ba;
};
const wordArrayToByteArray = function(wordArray, length) {
if (wordArray.hasOwnProperty("sigBytes") && wordArray.hasOwnProperty("words")) {
length = wordArray.sigBytes;
wordArray = wordArray.words;
}
let result = [];
let bytes;
let i = 0;
while (length > 0) {
bytes = wordToByteArray(wordArray[i], Math.min(4, length));
length -= bytes.length;
result.push(bytes);
i++;
}
return [].concat.apply([], result);
};
Solution
Thanks for the answers bellow, this was the solution.
...
while (length > 0) {
bytes = wordToByteArray(wordArray[i], Math.min(4, length));
length -= bytes.length;
bytes.forEach(function (byte) {
result.push(byte);
});
i++;
}
return result;
};
[].concat.apply([], result); is likely your problem; that's effectively saying "please call [].concat(result[0], result[1], result[2], ..., result[result.length - 1]). For a large input, you likely have a proportionately large result. Per MDN's warnings on apply:
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.
If your result array is truly huge, trying to pass tens of thousands (or more) arguments to Array.concat could blow your stack. The MDN docs suggest that in scenarios like yours you could use a hybrid strategy to avoid blowing the stack, applying to chunks of arguments at a time instead of all the arguments, no matter how big.
Luckily, someone has already provided a guide on how to do this safely; just use that and explicitly loop to extend with each sub-array in result.
Your result array is very large. Problem here is in apply method where you provide result as arguments. Arguments of a function are pushed onto stack and it causes stack overflow.

How does this if-else statement inside a function work?

I've been completing challenges on FreeCodeCamp and stumbled upon this solution for an algorithm. Can't comprehend how the if else statement works here.
function chunkArrayInGroups(arr, size) {
var temp = [];
var result = [];
for (var a = 0; a < arr.length; a++) {
if (a % size !== size - 1)
temp.push(arr[a]);
else {
temp.push(arr[a]);
result.push(temp);
temp = [];
}
}
if (temp.length !== 0)
result.push(temp);
return result;
}
Why is temp = [] at the end of the else block?
temp = [] means "reset the temp variable to an empty array"
in the if block, the arr[a] element is pushed at the end in the temparray.
in the else block, the same happens AND the whole current temp array is added at the end of the big result array of arrays, and the temparray is reset to the empty array.
Cannot say much more since there is not data or context written in your question. Hope this has answered your question.
The function divides an array into chunks of small arrays where the 'size' parameter defines the length of each chunks array.
The algorithm works as follows:
iterate each array element (for loop) until main_array elements index
is less than chunk array size (a % size !== size - 1), and push elements into
temporary array.
else block - push element into temp array, push temp array into results array
and create new empty temp array ();
at the end if temp array length is not 0 then push it also into results array.
that's it :)
Seems like the code tries to divide an array into chunks of smaller arrays of size size. Let's say you have a big bag full of apples and you want to reduce it to smaller apple bags each having at max 2 apples. How would you do in real life? You will go through the apples starting from first one and keep putting them in a small bag until it gets full. Then you move to another small bag and start to fill that it. Same thing is done here.
Inside if there is a condition checking if the temporary chunks have been filled and if not then keep pushing to the current temporary array. If yes then create a new array and keep pushing to it. temp=[] ensures the resetting.
It happens #Peter Pavluchenko. Let's walk through the code and see what it's doing.
the caller is specifying the size of chunks.
a % size !== size -1
% is a mod operator. so when a is divided by size what is the result? is the result not equal to size -1?
if the result of 'a' being divided by size is not equal to size -1, than we push the item to the end of our temp array.
If the result of 'a' being divided by size is equal to size - 1, we have found our first chunk! So lets' add the item to our temp array and add the temp array to our result array.
temp array
- item 1
- item 2
result array
- item
-- item 1
-- item 2
so when you do result[0], you will get another array.
chunk = result[0];
chunk[0] = the actual object you might be interested in.
After we have pushed the temp array to the end of our result array, we need to reset it so we can start collecting items for the next chunk.
The last 'if' is looking for situations like, you are in the middle of a chunk, but it's not a whole chunk as specified by the size argument. so we have some objects in our temp array that we need to add to the result array before we leave the method.
It appears that the task is to split up a given array (arr) into an array of arrays, of which each array has length size - except perhaps for the last which has any "leftovers" if the length of array is not an exact multiple of size.
The way the given solution works is to go through arr, pushing its elements into the temporary array temp. But every size steps - this corresponds to the else condition inside the for loop - the temp array will have the correct length (namely size), so then the entire temp array, as a single element, is appended to the result array, and then returned to being an empty array to start over.
This way, result ends up with the chunks of size size as required. The final if statement just adds the "leftovers" on - that is, anything left in temp. (But we check that it isn't empty, otherwise if the length was an exact multiple you'd have an unwated empty array at the end of result.)

javascript syntax question about splice in this statement -> copy.push(array.splice(i,1)[0])

So this is a syntax question, I have been reading the MDN docs but i can't find a similar example. I got this particular code snippet from here: https://bost.ocks.org/mike/shuffle/ as I was reading on randomizing arrays.
Here is the full function:
function shuffle(array) {
var copy = [], n = array.length, i;
// While there remain elements to shuffle…
while (n) {
// Pick a remaining element…
i = Math.floor(Math.random() * n--);
// And move it to the new array.
copy.push(array.splice(i, 1)[0]);
}
return copy;
}
Now, I have used it and modified it to fit my needs and it worked fine (although the splicing was a nightmare because it kept destroying my original data) but the point here is this line:
copy.push(array.splice(i, 1)[0]);
I hate not understanding something that would seem basic, but what is the [0] doing in this case? If i take it out the whole thing breaks, but im not sure if it;s part of the splice, or the push, or if its an index of the new array or the spliced array. IF anyone can help shed some light on this i would be very grateful!
Let's break down
copy.push(array.splice(i, 1)[0]);
in the order that it is "executed"
array.splice(i, 1);
array.splice(n, m) removes m elements starting and index n and returns these removed elements as an array ... so in this case you get an array of length 1 - let's call it x
substituting x for array.splice(i, 1) in the original code, we now have
copy.push(x[0]);
x[0] is the one and only element removed from array - which is pushed on to copy array
to prevent the incoming array from being mutated
function shuffle(array) {
var copy = [], n = array.length, i;
array = array.slice();
// rest of code
}
By the way, you could also have done
copy.push(array.splice(i, 1).pop());
or
copy.push(array.splice(i, 1).shift());

javascript - Issue in the loop in algorithm exercise

I'm studing an algorithm problem and I did understand all the things except the lines that I marked up with comment into the code.
Note: It is not to solve the code. The code is working good. It is only to explain me about what is the marked lines' purposes.
let arr= [40, 50, 80, 20, 21, 35]
function qSort(arr) {
if(arr.length == 0){ // <-- These lines. I did NOT understand its purpose
return [];
}
var left = []
var right = []
var pivot = arr[0]
for(var i= 1; i<arr.length; i++) {
if(arr[i] < pivot) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return qSort(left).concat(pivot, qSort(right))
}
console.log(qSort(arr))
This code is working good, but When I comment these lines into the code, the script doesn't work and it cause an error message referencing a loop issue: Uncaught RangeError: Maximum call stack size exceeded
Does anybody explain it to me please? I would like to know what is this lines' purpose. Thanks
If the given array has a length of 0 (is empty), just return an empty array, because there is no sorting to be done.
Actually the line could be:
if(arr.length <= 1){
return arr;
}
since for 0 and 1 elements the's no sorting to be done.
This is related to the function being recursive (it calls itself) and therefor it needs a case, at wich it stops calling itself and return a value. That's called a recursion anchor.
This is called the base case of recursion, which ends the recursion. If you leave it away, qSort will call qSort will call qSort will call...
This line was written for optimization.
If there is no data in array you have nothing to sort.
Plus when you sort elements you are passing smaller and smaller chunks of base array to qSort function, so you have to finish qSort execution at one point.
So basically it tells the code "There is nothing to sort".

Correct terminology in nested for loop

I'm trying to store all prime numbers of a given number in an array. I'm running an inner loop from 0 to the count of the outer loop every time the outer loop increases by one. I have if statements inside the inner loop to determine if the number is prime.
function sumPrimes(num) {
var primes = [];
for (var i=0; i<num; i++){
for (var j=0; j<num[i]; j++){
if(num[i][j] !== 1){
if(num[i] % num[i][j] !== 0){
primes.push(num[i]);
}
}
}
}
return primes;
}
sumPrimes(10);
Currently this isn't returning anything. What is the correct way of targeting the counters? essentially I want to say - if (the [number of the outer loop count] is NOT divisible by any number bar 1 and itself from the inner loop that leaves no remainder) {
push this step on the outer loop into my 'primes' array
}
Edit: im not seeking better solutions on how to fill up a prime number array of which im sure there is many ill learn after I solve this. I am right now using this problem as an example for nesting for loops and how to correctly call the different parameters.

Categories