Javascript code causing IE freeze - javascript

I have the below code causing Internet Explorer to freeze. It's a project that involves processing student grades as an assignment:
var array1 = StudentGradeAreadHugeList();
var nextArrayItem = function() {
var grade = array1.pop();
if (grade) {
nextArrayItem();
}
};
i hope you can help me with this.

You could show more info about the application you're trying to do. But I believe it's a matter of stack overflow (maybe you're using a big list). So, to overcome that you should modify the "nextArrayItem":
window.setTimeout (nextArrayItem, 0)
The freeze incurring mainly from the big data, but now the Event Loop will handle the Recursion process and not your Call Stack.

This is likely caused by an endless recursion. Be aware of proper handling of return values in IE:
var array1 = StudentGradeAreadHugeList();
var nextArrayItem = function() {
var grade = array1.pop();
if ( grade !== null && typeof(grade) !== "undefined" ) {
nextArrayItem();
}
};
pop() on an empty array will not return boolean false but a typeless "undefined".

There's two problems here:
You might be exceeding the call stack limit
Your if-conditional is set-up incorrectly
For the first issue:
As one of the previous responders mentioned, if you have a very large list you can exceed the limit of the call stack since you need to do a recursive call for each element. While doing setTimeout might work, it feels like a hack-y solution. I think the real issue is that your function is handling the array recursively rather than iteratively. I would recommend re-writing your function using a for-loop.
For the second issue:
Let's say in this case your array was set to [100, 90, 80]. When you invoke nextArrayItem() it will work properly the first two time, but the third time you call nextArrayItem() you are popping off the last remaining item (in this case 100) and your grade will be set to 100 which is a truthy value. Therefore, your if-conditional will pass and your function erroneously try to invoke itself again despite the fact that your array is now empty and the program should now exit the call stack.
I tried testing your code using my example in Chrome and what happens is that it will recurse one too many times and invoke pop on an empty array, which will return undefined.
You can fix this issue by changing the if conditional to check for the last element in the array after you have popped the array.
See revised code:
var nextArrayItem = function() {
var grade = array1.pop();
if (array1[array1.length-1]) {
nextArrayItem();
}
};

Related

Why is my script saying '(' === ')' is true?

I was doing this kata on codewars. The question wants the function to return true if the first argument (string) passed in ends with the 2nd argument (also a string). So I wrote my function and everything worked just fine until it compares ':-)' with ':-(' and returns true.
What is wrong? I'm so confident that my code should work that I don't even know what to search for.
function solution(str, ending){
if (!ending) return true; // if ending is a empty string return true (the question wants that)
let ok;
const strArr = str.split(''), endingArr = ending.split('');
for (let i = 0; i < endingArr.length; i++) strArr.reverse()[i] === endingArr.reverse()[i] ? ok = true : ok = false;
return ok;
}
console.log(solution(":-)",":-("));
Your problem is a misunderstanding of what reverse() does. It does not return a reversed copy of the old array, it reverses the existing array and returns that same array. As a result, you keep reversing the arrays back and forth every iteration of the loop, causing some elements to be skipped and some to be checked twice.
Array.prototype.reverse() on MDN
Edit:
As pointed out by others in the comments, both to the question and this answer, there are in fact multiple problems.
reverse() aside, the loop always sets ok to the result of the last comparison, making the function ignore all previous results.
The easier way to implement this is to remove ok altogether. Instead, return false as soon as a mismatch is detected. If the function runs long enough to exit the loop, it means no mismatch was detected and true can be returned.
Edit 2:
Just as a friendly suggestion:
While both reverse() and ok are real issues with the code, I only noticed the first one the first time around due to the formatting of the code. The ok problem was off-screen due to the line being too long. As such, once I spotted the reverse() issue, I assumed that was it and didn't bother scrolling sideways to see the rest of the code.
I am not going to demand that you write your own code in a certain way, but if you format it properly, it allows others to read it more easily. In essence, you help us to more easily help you.
For instance, this line:
for (let i = 0; i < endingArr.length; i++) strArr.reverse()[i] === endingArr.reverse()[i] ? ok = true : ok = false;
...would have been significantly easier to read as...
for (let i = 0; i < endingArr.length; i++) {
if(strArr.reverse()[i] === endingArr.reverse()[i])
ok = true;
else
ok = false;
}
...or some variation thereof. Here, the problem is significantly more visible and obvious.
The other answer explains many of the mistakes you've made. I wanted to point out just how much you've over-thought your solution.
function solution(str, ending){
if (ending === "") return true; // if ending is a empty string return true (the question wants that)
return str.endsWith(ending);
}
console.log(solution(":-)",":-("));
console.log(solution("foo",""));
console.log(solution("foo","bar"));
console.log(solution("foobar","bar"));
Even my solution above is overengineered, str.endsWith("") always returns true. So this can be simplified further.
function solution(str, ending){
return str.endsWith(ending);
}
console.log(solution(":-)",":-("));
console.log(solution("foo",""));
console.log(solution("foo","bar"));
console.log(solution("foobar","bar"));

Cost: calling if vs calling function

I am trying to come up with an optimized solution where I need to loop through 1000's of records in an excel sheet.
I have scenarios like:
if (!lei.renewalDate) {
lei.renewalDate = new Date(record.renewalDate).getTime();
}
if (lei.isManagedByGIDM === undefined || lei.isManagedByGIDM === null) {
lei.isManagedByGIDM = this.organiseManagedByData(record.managedByOthers);
}
My question is, does it make any sense in checking the existence of renewalDate or can I directly call
lei.renewalDate = new Date(record.renewalDate).getTime();?
I am adding an extra check to if I need to call a such small built-in function. I am doing the same in the second condition check, to determine if I need to call in organiseManagedByData method (which is a small function with no other function call in it).
I am trying to learn time and space complexity.
Is there a way I can determine the cost of the above code

Restricted JavaScript Array Pop Polyfill not working

I'm creating a few specific functions for a compiler I'm working on, But certain restrictions within the compiler's nature will prevent me from using native JavaScript methods like Array.prototype.pop() to perform array pops...
So I decided to try and write some rudimentary pseudo-code to try and mimic the process, and then base my final function off the pseudo-code... But my tests seem to fail... based on the compiler's current behavior, it will only allow me to use array.length, array element assignments and that's about it... My code is below...
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
y+1<z?(x[y]=arr[y]):(w=arr[y],arr=x);
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // undefined... should be 2
console.log(Arr); // [-1,0,1,2]... should be [-1,0,1]
I'm trying to mimic the nature of the pop function but can't seem to put my finger on what's causing the function to still provide undefined and the original array... undefined should only return if an initial empty array is sent, just like you would expect with a [].pop() call...
Anyone have any clues as to how I can tailor this code to mimic the pop correctly?
And while I have heard that arr.splice(array.length-1,1)[0]; may work... the compiler is currently not capable of determining splice or similar methods... Is it possible to do it using a variation of my code?
Thanks in advance...
You're really over-thinking [].pop(). As defined in the specs, the process for [].pop() is:
Get the length of the array
If the length is 0
return undefined
If length is more than 0
Get the item at length - 1
Reduce array.length by 1
Return item.
(... plus a few things that the JavaScript engine needs to do behind the scenes like call ToObject on the array or ensure the length is an unsigned 32-bit integer.)
This can be done with a function as simple as the one below, there's not even a need for a loop.
function pop(array) {
var length = array.length,
item;
if (length > 0) {
item = array[length - 1];
array.length -= 1;
}
return item;
}
Edit
I'm assuming that the issue with the compiler is that Array.prototype.pop isn't understood at all. Re-reading your post, it looks like arrays have a pop method, but the compiler can't work out whether the variable is an array or not. In that case, an even simpler version of this function would be this:
function pop(array) {
return Array.prototype.pop.call(array);
}
Try that first as it'll be slightly faster and more robust, if it works. It's also the pattern for any other array method that you may need to use.
With this modification, it works:
http://jsfiddle.net/vxxfxvpL/1/
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
if(y+1<z) {
(x[y]=arr[y]);
} else {
(w=arr[y],arr=x);
break;
}
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // 2
The problem now is to remove the last element. You should construct the original array again without last element. You will have problems with this because you can't modify the original array. That's why this tasks are maded with prototype (Array.prototype.pop2 maybe can help you)

Programming optional ignorance

In Javascript what is the best way to handle scenarios when you have a set of arrays to perform tasks on sets of data and sometimes you do not want to include all of the arrays but instead a combination.
My arrays are labeled in this small snippet L,C,H,V,B,A,S and to put things into perspective the code is around 2500 lines like this. (I have removed code notes from this post)
if(C[0].length>0){
L=L[1].concat(+(MIN.apply(this,L[0])).toFixed(7));
C=C[1].concat(C[0][0]);
H=H[1].concat(+(MAX.apply(this,H[0])).toFixed(7));
V=V[1].concat((V[0].reduce(function(a,b){return a+b}))/(V[0].length));
B=B[1].concat((MAX.apply(this,B[0])-MIN.apply(this,B[0]))/2);
A=A[1].concat((MAX.apply(this,A[0])-MIN.apply(this,A[0]))/2);
D=D[1].concat((D[0].reduce(function(a,b){return a+b}))/(D[0].length));
S=S[1].concat((S[0].reduce(function(a,b){return a+b}))/(S[0].length));
}
It would seem counter-productive in this case to litter the code with tones of bool conditions asking on each loop or code section if an array was included in the task and even more silly to ask inside each loop iteration with say an inline condition as these would also slow down the processing and also make the code look like a maze or rabbit hole.
Is there a logical method / library to ignore instruction or skip if an option was set to false
All I have come up with so far is kind of pointless inline thing
var op=[0,1,1,0,0,0,0,0]; //options
var L=[],C=[],H=[],V=[],B=[],A=[],D=[],S=[];
op[0]&&[L[0]=1];
op[1]&&[C[0]=1,console.log('test, do more than one thing')];
op[2]&&[H[0]=1];
op[3]&&[V[0]=1];
op[4]&&[B[0]=1];
op[5]&&[A[0]=1];
op[6]&&[A[0]=1];
It works in that it sets only C[0] and H[0] to 1 as the options require, but it fails as it needs to ask seven questions per iteration of a loop as it may be done inside a loop. Rather than make seven versions of the the loop or code section, and rather than asking questions inside each loop is there another style / method?
I have also noticed that if I create an array then at some point make it equal to NaN rather than undefined or null the console does not complain
var L=[],C=[],H=[],V=[],B=[],A=[],D=[],S=[];
L=NaN;
L[0]=1;
//1
console.log(L); //NaN
L=undefined;
L[0]=1
//TypeError: Cannot set property '0' of undefined
L=null
L[0]=1
//TypeError: Cannot set property '0' of null
Am I getting warmer? I would assume that if I performed some math on L[0] when isNaN(L)===true that the math is being done but not stored so the line isn't being ignored really..
If I understand what you want I would do something like this.
var op = [...],
opchoice = {
//these can return nothing, no operation, or a new value.
'true': function(val){ /*operation do if true*/ },
'false': function(val){ /*operation do if false*/ },
//add more operations here.
//keys must be strings, or transformed into strings with operation method.
operation: function(val){
//make the boolean a string key.
return this[''+(val == 'something')](val);
}
};
var endop = [];//need this to prevent infinite recursion(loop).
var val;
while(val = op.shift()){
//a queue operation.
endop.push(opchoice.operation(val));
}
I'm sure this is not exactly what you want, but it's close to fulfilling the want of not having a ton of conditions every where.
Your other option is on every line do this.
A = isNaN(A) ? A.concat(...) : A;
Personally I prefer the other method.
It looks like you repeat many of the operations. These operations should be functions so at least you do not redefine the same function over and over again (it is also an optimization to do so).
function get_min(x)
{
return +(MIN.apply(this, a[0])).toFixed(7);
}
function get_max(x)
{
return +(MAX.apply(this, a[0])).toFixed(7);
}
function get_average(x)
{
return (x[0].reduce(function(a, b) {return a + b})) / (x[0].length);
}
function get_mean(x)
{
return (MAX.apply(this, x[0]) - MIN.apply(this, x[0])) / 2;
}
if(C[0].length > 0)
{
L = L[1].concat(get_min(L));
C = C[1].concat(C[0][0]);
H = H[1].concat(get_max(H));
V = V[1].concat(get_average(V));
B = B[1].concat(get_mean(B));
A = A[1].concat(get_mean(A);
D = D[1].concat(get_average(D));
S = S[1].concat(get_average(S));
}
You could also define an object with prototype functions, but it is not clear whether it would be useful (outside of putting those functions in a namespace).
In regard to the idea/concept of having a test, what you've found is probably the best way in JavaScript.
op[0] && S = S[1].concat(get_average(S));
And if you want to apply multiple operators when op[0] is true, use parenthesis and commas:
op[3] && (V = V[1].concat(get_average(V)),
B = B[1].concat(get_mean(B)),
A = A[1].concat(get_mean(A));
op[0] && (D = D[1].concat(get_average(D)),
S = S[1].concat(get_average(S)));
However, this is not any clearer, to a programmer, than an if() block as shown in your question. (Actually, many programmers may have to read it 2 or 3 times before getting it.)
Yet, there is another solution which is to use another function layer. In that last example, you would do something like this:
function VBA()
{
V = V[1].concat(get_average(V));
B = B[1].concat(get_mean(B));
A = A[1].concat(get_mean(A));
}
function DS()
{
D = D[1].concat(get_average(D));
S = S[1].concat(get_average(S));
}
op = [DS,null,null,VBA,null,null,...];
for(key in op)
{
// optional: if(op[key].hasOwnProperty(key)) ... -- verify that we defined that key
if(op[key])
{
op[key](); // call function
}
}
So in other words you have an array of functions and can use a for() loop to go through the various items and if defined, call the function.
All of that will very much depend on the number of combinations you have. You mentioned 2,500 lines of code, but the number of permutations may be such that writing it one way or the other will possibly not reduce the total number of lines, but it will make it easier to maintain because many lines are moved to much smaller code snippet making the overall program easier to understand.
P.S. To make it easier to read and debug later, I strongly suggest you put more spaces everywhere, as shown above. If you want to save space, use a compressor (minimizer), Google or Yahoo! both have one that do a really good job. No need to write your code pre-compressed.

What is the initial value of an initialized Array in Javascript on different systems?

I've been struggling for months with a JavaScript problem where I have an array with a few properties, and later some of these properties are checked to decide whether or not to show a message to the user.
Now this all goes well on most systems (especially more recent browsers) but not so much on some of my client's IE7 computers.
Now I just found out that somewhere in my code I initialize a new Array like below, but never actually set the value for 'done'
var qar=new Array('question_no','pos','done');
qar['question_no'] = 1234;
qar['pos'] = 1234; //dont mind these numbers
Later in some for loop I check:
//check if this question was already shown
if(qar['done'])
continue; //stop here, don't show message
//set done to true, so that this question will not be shown again
qar['done'] = true;
window.alert('messaged!');
Again, what goes wrong is that sometimes (actually pretty often, but not always) the message is not shown at all in IE7.
Now for my question to you: I know that qar['done'] should be undefined right after initialization, which makes my code work fine (in Chrome etc), but could it be that somehow in IE7 this situation is handled differently? For example that qar['done'] is not undefined, but some random value and therefore is sometimes considered true accidentally? Or is that a stupid thing to think?
If this is not the problem, then I don't know what is..
Thanks in advance!
By doing this:
var qar=new Array('question_no','pos','done');
You are just creating array with indexes.
qar[0] will be 'question_no'
qar[1] will be 'pos'
qar[2] will be 'done'
in this case, qas['done'] will always be undefined.
Thats why its causing problems. You should either use javascript object instead of using an array.
But you can do something like this:
if(typeof qar['done'] === 'undefined'){
qar['done'] = true;
alert('messaged!');
}
Your code should be like this:
var qar={};
qar['question_no'] = 1234;
qar['pos'] = 1234; //dont mind these numbers
//check if this question was already shown
if(!qar['done']) {
//set done to true, so that this question will not be shown again
qar['done'] = true;
window.alert('messaged!');
}

Categories

Resources