Function worked, now RangeError - javascript

Why am I getting a RangeError: Maximum call stack exceeded error? I am trying to parse through text to find math and solve it. It was working until I started to implement parenthesis'. I have tried to find the error but I just can't figure it out.
My Code:
var alg = {
calc: function(eq, solveFor) {
var out;
var sideOne = eq.substring(0, eq.indexOf('='))
var sideTwo = eq.substring(eq.indexOf('=') + 1)
if (sideOne === solveFor) {
alg.simplify(sideTwo);
}
if (sideTwo === solveFor) {
alg.simplify(sideOne);
}
},
simplify: function(eq) {
str = $.trim(eq);
if (str == undefined) {
console.error('Error: null string')
} else {
var charMatch = /^[\d\*\/\+\-\^\(\) ]+$/
if (charMatch.exec(str) === null) {
console.error('Error: Invalid char/expression')
} else {
alg.parMath('not');
alg.expRoot(solve);
alg.multDiv(solve);
alg.addSubtr(solve);
}
}
},
fromPar: function(par) {
alg.parMath(par);
alg.expRoot(solve);
alg.multDiv(solve);
alg.addSubtr(solve);
},
parMath: function(source) {
var reP = /\(([\d\*\/\+\-\^\(\) ]+)\)/
var exP = reP.exec(str)
if (source === 'par') {
str = str.replace(exP[0], solve)
}
if (exP !== null) {
use = 'par'
solve = exP[1]
} else {
use = 'not'
solve = str;
}
},
expRoot: function() {
var fracCon = /(\d+)\/(\d+)/
var reER = /(\d+)(\^)(\d+(\/\d)?)(?!\/)/
var exER = reER.exec(solve)
if (exER !== null) {
var exFC = fracCon.exec(exER[3])
if (exFC !== null) {
var rep = Math.pow(parseFloat(exER[1]),(parseFloat(exFC[1]) / parseFloat(exFC[2])))
} else {
var rep = Math.pow(parseFloat(exER[1]),parseFloat(exER[3]))
}
solve = solve.replace(exER[0], rep)
if (reER.exec(solve) !== null) {
alg.expRoot();
}
}
},
multDiv: function() {
var reMD = /(\d+(?:\.\d+)?) *([\*|\/]) *(\d+(?:\.\d+)?)/
var exMD = reMD.exec(solve);
if (exMD !== null) {
if (exMD[2] === "*") {
var rep = parseFloat(exMD[1]) * parseFloat(exMD[3]);
var rep = Math.round(rep * 1000000) / 1000000;
} else {
var rep = parseFloat(exMD[1]) / parseFloat(exMD[3]);
var rep = Math.round(rep * 1000000) / 1000000;
}
if (use !== 'par') {
solve = solve.replace(exMD[0], rep);
}
if (reMD.exec(solve) !== null) {
alg.multDiv();
}
}
},
addSubtr: function() {
var reAS = /(\d+(?:\.\d+)?) *([\+|\-]) *(\d+(?:\.\d+)?)/
var exAS = reAS.exec(solve); //Getting RangeError here
if (exAS !== null) {
if (exAS[2] === "+") {
var rep = parseFloat(exAS[1]) + parseFloat(exAS[3])
var rep = Math.round(rep * 1000000) / 1000000
} else {
var rep = parseFloat(exAS[1]) - parseFloat(exAS[3])
var rep = Math.round(rep * 1000000) / 1000000
}
if (use !== 'par') {
str = str.replace(exAS[0], rep)
}
if (exAS !== null) {
alg.addSubtr(solve);
} else {
if (use == 'not') {
out = solve;
} else {
alg.fromPar('par')
}
}
} else {
if (use == 'not') {
out = solve;
} else {
alg.fromPar('par')
}
}
}
};
console.log(alg.calc('x=(1+1)', "x"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I'm getting the error at the start of addSubtr function (marked by a comment). Can anyone help me find how to fix the error?

The problem is that your code goes into an infinite loop. Here is the relevant part of the logic
addSubtr: function() {
/* ommitted */
var exAS = reAS.exec(solve); //Getting RangeError here
if (exAS !== null) {
/* ommitted - logic here*/
if (exAS !== null) {
alg.addSubtr(solve);
/* ommitted */
}
}
}
You get the value for exAS by parsing solve through a regex.
If this returns a non-null value you've gotten a match
With that hen you go inside the if condition and do some logic
Still inside there, there is another if statement that checks if the regex matched anything. Now, by definition, this would be true - it can be easily seen with a lot of code removed - the same condition is checked for twice. There is nothing that would change the the outcome between the two ifs.
Since the conditional check passes you recursively call the same function again with the same input.
Because the input is the same, the logic will work the same so steps 1-5 are executed again and the function is called again.
This causes infinite recursion. Well, in reality there is a limit and that's the stack size for JavaScript, which is why you are getting the error. It's a bit misleading, since it's the regex that runs over the call stack size, not the recursive call to addSubtr, else it would have been a bit more clear what is going on.
For how to fix it - you need to restructure the logic so you don't get into infinite loops. I am not sure exactly what is the best way for your case but I'd suggest working it out yourself - it would be a useful exercise regardless. Here are some pointers
In point 4. I made, I mentioned that there was an essentially useless check. I assume that it is supposed to be useful.
- You may have intended the inner if to be outside of the outer one. As it stands now, the two are equivalent so the inner if can just be removed.
- maybe the condition of the inner if is incorrect - it could be that you only sometimes want to do the recursive call, not every time.
- perhaps there was supposed to be something that changes either exAS or solve or both. Thus either the condition would (potentially) yield a different result the second time it's checked, or the function would produce a different result when called recursively (which would make the recursive call useful) or both.

Your main problem is that you have an infinite loop which is here
if (exAS !== null) {
alg.addSubtr(solve);
}
This is a useless check for two reasons. One because as you notcie exAS is defined in addSubtr. So every time you call this function you are resetting the value.
Your other problems are you are create variables with the same names, and relying off of static variables/ void functions.
If pm me i cant help you workout the correct way to structure this this function and all the other functions .

Related

How to get around this long overflow error in a loop method with conditionals?

I am still learning my way around JS and have run into this issue that I cannot figure out. I have two sets of array of json objects that I have simplified as an example here. I wrote a method to basically compare the two and make sure the values match with each other. It looks like the following:
var actual = {
"nameTag": "abc123",
"number": "567553218",
"hitDate": "2022-12-09"
};
var expected = {
"nameTag": "abc123",
"number": "567553218",
"hitDate": "2022-12-09"
};
function matchVar(actual, expected) {
var array = [];
var now = new Date();
var dateNow = new Date(now.getTime() + now.getTimezoneOffset() * 60000);
for (key in actual) {
var checkForMatch = false;
var actualDate = Date.parse(actual[key]);
if (isNaN(actualDate) || !(isNaN(actual[key]))) {
if (expected.hasOwnProperty(key)) {
var tempMatch = console.log("There is a match!");
} else {
continue;
}
} else {
if (actualDate <= dateNow) {
checkForMatch = true;
} else {
checkForMatch = false;
}
}
array.push(checkForMatch);
}
return (array);
}
console.log(matchVar(actual, expected));
When I run the code here, it gives the correct result back. However, when I run on my local machine and/or on docker, I get the following error:
org.graalvm.polyglot.PolyglotException: java.lang.ArithmeticException: long overflow
- java.base/java.lang.Math.multiplyExact(Math.java:1004)
- java.base/java.lang.Math.multiplyExact(Math.java:980)
- java.base/java.time.Instant.toEpochMilli(Instant.java:1236)
- com.oracle.truffle.js.runtime.builtins.JSDate.makeDay(JSDate.java:502)
- com.oracle.truffle.js.runtime.builtins.JSDate.makeDate(JSDate.java:522)
- com.oracle.truffle.js.builtins.DateFunctionBuiltins$DateParseNode.parse(DateFunctionBuiltins.java:113)
- com.oracle.truffle.js.builtins.DateFunctionBuiltinsFactory$DateParseNodeGen.execute(DateFunctionBuiltinsFactory.java:51)
The error seems to be happening on the "number" key in the loop. Please advise.
Thank you in advance!

Javascript - dont compare if condition met

I have this script for my html table which colors the cells in column row by row:
$(document).ready( function () {
var table, aP, rtvP, mmP, meP;
table = document.getElementById('productTable');
for (i = 1; i < table.rows.length; i++) {
var rowCells = table.rows.item(i).cells;
if (rowCells[3].firstChild.data !== '—') {
aP = parseFloat(rowCells[3].firstChild.data);
} else {
aP = 0;
}
if (rowCells[5].firstChild.data !== '—') {
rtvP = parseFloat(rowCells[5].firstChild.data);
} else {
rtvP = 0;
}
if (rowCells[7].firstChild.data !== '—') {
mmP = parseFloat(rowCells[7].firstChild.data);
} else {
mmP = 0;
}
if (rowCells[9].firstChild.data !== '—') {
meP = parseFloat(rowCells[9].firstChild.data);
} else {
meP = 0;
}
console.log(aP, rtvP, mmP, meP);
if (aP > rtvP || mmP || meP) {
rowCells[3].bgColor = 'red';
} else if (aP === rtvP || mmP || meP) {
rowCells[3].bgColor = 'yellow';
} else {
rowCells[3].bgColor = 'green';
}
}
})
I know that is a little monster, but the only thing I miss is that, there should be no comparison for value if that value = 0. Ex. If aP = 100, rtvP = 150, mmP = 0, meP = 50, value of mmP should be skipped in comparison. Is there easy way to do that? I don't want to make another spaghetti of IFs
You could try converting one of those instances to a proper function:
function convertP(value) {
if (value !== '—') {
return parseFloat(value);
}
return 0;
}
Then you can call it like:
meP = convertP(rowCells[9].firstChild.data);
Where you can augment that convertP function to be "smarter" and handle different cases later.
Now for your comparison problem you probably want to store these converted values into an array instead of as a bunch of unrelated variables, like:
var points = [ ];
[ 3, 5, 7, 9 ].forEach(function(i) {
points.push(convertP(rowCells[i].firstChild.data));
});
Where now you have all of them in one neat, tidy container. You can then compare them quickly by doing something like:
var diffs = [ ];
points.forEach(function(p) {
diffs.push(aP - p);
});
The key thing to remember here is that floating point values are often approximations so it's important to not depend on them being precisely equal. 1.0 + 2.0 does not necessarily === 3.0. There's going to be a tiny amount of deviation due to floating point quirks, so go with +/- some tiny value, even if that value is 0.000001.
Now you can identify your color condition with a function:
function colorCondition(aP, points) {
var state = 'green';
var epsilon = 0.000001;
points.forEach(function(p) {
if (aP > (p + epsilon)) {
state = 'red';
}
else if (aP > (p - epsilon)) {
state = 'yellow';
}
});
return state;
}
Now you have a generic solution that can work with N inputs. This is the goal of programming when you're trying to adhere to the Zero, One or Infinity Rule.
One of the ways to put a prerequisite condition in if is using the logical AND operator: &&.
For your variables it will be something like:
if (mmP!=0 && mmP>aP){}
This way if mmP is 0, first condition will return false and second condition won't be processed.
You can enforce a condition without making another spaghetti of IFs.
I'm not sure I understood your use case, so if that does not work for you just comment it.

javascript for loop is not incrementing

I am trying to using a for loop for trying to validate the input of the user and this is the code i got.
function Valid() {
objfieldid = ["userMail", "userCont"]
objboxid = ["cancelMail", "cancelCont"]
return objfieldid.every(callnonvalid)
}
function callnonvalid(id) {
var valid = false
var objlength = objfieldid.length
objlength--;
for (var i = objlength; i >= 0; i--){
var cobj = document.getElementById(objboxid[i]).checked;
if (document.getElementById(id).value != "" ){
var obj = document.getElementById(id).value;
} else if (cobj == true) {
alert(i); //return 1, 1
return true
} else {
return false
}
}
}
As you can see, in the code, the for loop is running twice. but the i variable is left unchanged. Why would this happen?
btw, I did read different material about closure and i am sure there didnt got a closure problem
EDIT:guys please note that i did noticed the array is zero based, and i did minus the objlength by one.
Mistakes were found after checking the code carefully. The Mistake that I made was that I should not use the return for the out since that would stop the function from working, however that array.every Called the function twice which make the i not decreasing
I'm not sure why you're decrementing in your loop, because the performance gain would be infinitesimally small (it may even be slower, e.g. in Chrome/V8) and working in reverse order can get confusing further down the line, but that's your choice and I don't know enough about what you're doing to judge.
Either way, I don't think you'd want to decrement objlength before the loop begins as you are doing now. The whole point of a loop is to handle the incrementing/decrementing in the condition statement of the loop.
You would only decrement manually like that if you were going to move your if/else if/else statement into a closed over function and execute it recursively, decrementing the objlength from within the closure. Which would work, but it's unnecessarily complicated for what you're doing and you would gain nothing for rewriting the whole thing.
So, sticking with the looping approach, perhaps try either of these:
function Valid() {
objfieldid = ["userMail", "userCont"];
objboxid = ["cancelMail", "cancelCont"];
return objfieldid.every(callnonvalid);
}
function callnonvalid(id) {
var valid = false;
var objlength = objfieldid.length;
for(var i = 0; i < objlength; i++){
var cobj = document.getElementById(objboxid[i]).checked;
if (document.getElementById(id).value != "" ){
var obj = document.getElementById(id).value;
} else if (cobj == true) {
alert(i);
return true;
} else {
return false;
}
}
}
or, if you want to decrement, use while instead of for:
function Valid() {
objfieldid = ["userMail", "userCont"];
objboxid = ["cancelMail", "cancelCont"];
return objfieldid.every(callnonvalid);
}
function callnonvalid(id) {
var valid = false;
var i = objfieldid.length;
while(i--){
var cobj = document.getElementById(objboxid[i]).checked;
if (document.getElementById(id).value != "" ){
var obj = document.getElementById(id).value;
} else if (cobj == true) {
alert(i);
return true;
} else {
return false;
}
}
}
Because the array objboxid[] has only two elements, the first time through your loop objboxid[2] will be attempting to fetch an array index that is out-of-bounds.
You probably meant something like:
for (var i = objlength; i > 0; i--){
var cobj = document.getElementById(objboxid[i-1]).checked;
or perhaps
for (var i = objlength-1; i >= 0; i--){
var cobj = document.getElementById(objboxid[i]).checked;

Javascript object members

var math = {
Factorial:function(n) {
if (n == 0) {
return 1;
}
return n * Factorial(n - 1);
},
CalculateFactorial:function() {
var myValue = document.getElementById('myTextBox').value;
if (myValue.length == 0) {
alert("Please input the correct value!");
return;
}
var result = Factorial(myValue);
document.getElementById('title').innerHTML = result;
}
}
Hello guys. New to JavaScript and testing different things, coming from C# language things are pretty the same yet different.
I am trying to do something and no success.
Having the script above attached to a simple html button , trying to call the CalculateFactorial , math.CalculateFactorial(); , but what is wierd is that CalculateFactorial() doesn't actually see the Factorial() method. But in the math object I can see both methods. Wierd.
"JavaScript runtime error: 'Factorial' is undefined"
A good practice in JavaScript is to name all of your functions even if you assign them to a variable or use them like object's property. In your case just give a name to your Factorial function like this:
Factorial:function factorial(n)
{
if (n == 0)
{
return 1;
}
return n * factorial(n - 1);
},
For the problem of not seeing Factorial in your CalculateFactorial function just use this.Factorial when you invoke it.
This should fix the issue... if you want Factorial to be an internal only function (just a utility for the exposed CalculateFactorial) then you can do so this way:
var math = new function() {
var $this = this;
var Factorial = function(n) {
if (n == 0) {
return 1;
}
return n * Factorial(n - 1);
}
$this.CalculateFactorial = function() {
var myValue = document.getElementById('myTextBox').value;
if (myValue.length == 0) {
alert("Please input the correct value!");
return;
}
var result = Factorial(myValue);
document.getElementById('title').innerHTML = result;
}
}
The deeper design issue here is this is not particularly reusable javascript. You should consider pulling CalculateFactorial() out of the math object and into it's own handler... probably a click handler would be my guess like this:
var math = new function() {
var $this = this;
$this.Factorial = function(n) {
if (n == 0) {
return 1;
}
return n * $this.Factorial(n - 1);
}
}
$("#calcButton").click(function() {
var myValue = document.getElementById('myTextBox').value;
if (myValue.length == 0) {
alert("Please input the correct value!");
return;
}
var result = math.Factorial(myValue);
document.getElementById('title').innerHTML = result;
})
That happens beceause the Factorial method is part of the math object, and it doesn't reside in the global scope. When you try to call it directly, the parser will try to find that method into the global scope, and then, it will thrown an ReferenceError.
Since you're using the method inside the object itself, you can use the this keyword, because it will look into the object's context and find the method Factorial.
Another way of calling the Factorial method is by using the object, e.g: math.Factorial.
Take a look at the example below:
var math = {
Factorial: function(n) {
return n === 0 ? 1 : n * this.Factorial(n - 1);
},
CalculateFactorial: function(txt) {
document.getElementById('title').innerHTML = this.Factorial(txt.value);
}
};
document.getElementById('myTextBox').addEventListener('input', function() { math.CalculateFactorial(this); });
<input type="text" id="myTextBox" placeholder="Fatorial">
<div id="title"></div>

Check for continuous order in array in javascript

var userInput = prompt('enter number here');
var number = new Array(userInput.toString().split(''));
if (number ????){ //checks if the number is in a continuous stream
alert(correct);
}
else{
alert(invalid);
}
In Javascript, what can I do at "????" to check if it is in a continuous order/stream? Also how can I do this so that it only checks for this order/stream after a specific index in the array? Meaning the user enters say "12345678901234" which would pop up correct, but "12347678901234" would pop up invalid?(note there are two 7's) For the second part "3312345678901234" would pop up correct, how can this be implemented?
You can make a function that checks any string for a stream of continuous/increasing alpha-numeric characters starting at a given index like this:
function checkContinuous(str, startIndex) {
startindex = startIndex || 0;
if (str.length <= startIndex) {
return false;
}
var last = str.charCodeAt(startIndex);
for (var i = startIndex + 1; i < str.length; i++) {
++last;
if (str.charCodeAt(i) !== last) {
return false;
}
}
return true;
}
If it's numbers only and wrapping from 9 back to 0 is considered continuous, then it's a little more complicated like this:
function checkContinuous(str, startIndex) {
// make sure startIndex is set to zero if not passed in
startIndex = startIndex || 0;
// skip chars before startIndex
str = str.substr(startIndex);
// string must be at least 2 chars long and must be all numbers
if (str.length < 2 || !/^\d+$/.test(str)) {
return false;
}
// get first char code in string
var last = str.charCodeAt(0);
// for the rest of the string, compare to last code
for (var i = 1; i < str.length; i++) {
// increment last charCode so we can compare to sequence
if (last === 57) {
// if 9, wrap back to 0
last = 48;
} else {
// else just increment
++last;
}
// if we find one char out of sequence, then it's not continuous so return false
if (str.charCodeAt(i) !== last) {
return false;
}
}
// everything was continuous
return true;
}
Working demo: http://jsfiddle.net/jfriend00/rHH4B/
No need for arrays, just back though the string one character at a time.
When you hit a 0, substitute 10, and continue until the number
is not one more than the previous one.
function continuousFromChar(str, start){
start= start || 0;
var i= 0, L= str.length, prev;
while(L){
c= +(str.charAt(-- L)) || 10; // use 10 for 0
prev=+(str.charAt(L- 1));
if(c-prev !== 1) break;
}
return start>=L;
}
var s= "3312345678901234";
continuousFromChar(s,2)
/* returned value: (Boolean)
true
*/
This will do the checking in real-time entry, but a similar principle could be used to check an entry on a button submit or similar. I was not 100% sure as to which way you wanted it, so I went for the live method.
HTML
<input id="stream" type="text" />
Javascript
window.addEventListener("load", function () {
document.getElementById("stream").addEventListener("keyup", function (evt) {
var target = evt.target;
var value = target.value;
var prev;
var last;
var expect;
target.value = value.replace(/[^\d]/, "");
if (value.length > 1) {
prev = parseInt(value.slice(-2, -1), 10);
last = parseInt(value.slice(-1), 10);
expect = prev + 1;
if (expect > 9) {
expect = 0;
}
if (last !== expect) {
target.value = value.slice(0, value.length - 1);
}
}
}, false);
});
On jsfiddle
By changing the value here
if (value.length > 1) {
You can change where the checking starts.
Update: Ok, so it is function that you want, and you insist that it splits the string into an array. Then using the above as a reference, you could convert it to something like this.
Javascript
window.addEventListener("load", function () {
var testStrings = [
"0123456789012",
"0123456789",
"0123455555",
"555012345678901234",
"0123455555"];
function test(string, offset) {
if (typeof string !== "string" || /[^\d]/.test(string)) {
return false;
}
var array = string.split("");
var prev;
var last;
var expect;
return !array.some(function (digit, index) {
if (index >= offset) {
prev = parseInt(array[index - 1], 10);
last = parseInt(digit, 10);
expect = prev + 1;
if (expect > 9) {
expect = 0;
}
if (last !== expect) {
return true;
}
}
return false;
});
}
testStrings.forEach(function (string) {
console.log(string, test(string, 1));
});
});
On jsfiddle
As your question does not fully specify all possibilities, the above will return true for an empty string (""), of course you can simply add a check at the very beginning for that.
I also do not perform any checking for a valid number for your offset, but again this is something simple that you can add.
Of course these are just one (two) of many possible solutions, but hopefully it will set your mind in the right direction of thought.
There are some good answers here, but I would like to show a slight variation. I think it is important to showcase some different aspects of JavaScript and separating interests in code.
Functions as first class objects are cool - the exact rules for "continuous" can be changed with only changing the predicate function. Perhaps we should allow skipping numbers? No problem. Perhaps we allow hex digits? No problem. Just change the appropriate follows function for the specific rules.
This can be implemented generically because strings support indexing. This will work just as well over other array-like objects with an appropriate follows function. Note that there are no string-specific functions used in the continuous function.
Code also on jsfiddle:
// returns true only iff b "follows" a; this can be changed
function follows_1Through9WithWrappingTo0(b,a) {
if (b === "1" && a === undefined) {
// start of sequence
return true;
} else if (b === "0" && a === "9") {
// wrap
return true;
} else {
// or whatever
return (+b) === (+a) + 1;
}
}
function continuous(seq, accordingTo, from) {
// strings can be treated like arrays; this code really doesn't care
// and could work with arbitrary array-like objects
var i = from || 0;
if ((seq.length - i) < 1) {
return true;
}
var a = undefined;
var b = undefined;
for (; i < seq.length; i++) {
b = seq[i];
if (!accordingTo(b, a)) {
return false; // not continuous
}
a = b;
}
return true;
}
function assert(label, expr, value) {
if (!(expr === value)) {
alert("FAILED: " + label);
}
}
var follows = follows_1Through9WithWrappingTo0;
assert("empty1", continuous("", follows), true);
assert("empty2", continuous("foobar", follows, 6), true);
assert("skip", continuous("331234", follows, 2), true);
assert("good 1", continuous("123456789", follows), true);
assert("good 2", continuous("12345678901234", follows), true);
assert("bad seq 1", continuous("12347678901234", follows), false);
assert("bad seq 2", continuous("10", follows), false);
// here a different predicate ensures all the elements are the same
var areAllSame = function (b, a) {
return a === undefined || a === b;
};
assert("same", continuous("aaaaa", areAllSame), true);
Note that the skipping could also be extracted out of the continuous function: in a language with better "functional" collection support, such as C#, this is exactly what I'd do first.

Categories