Related
this is a simple puzzle game using html 5 drag and drop to move spans to their correct spot. A timer starts on the first drag (function drag(e)), and stops when there are no more spans left in the reserve (not shown).
(side question : is there a standard way of beautifying the (m)m : ss timer output I want, or do I have to go on as I am?)
Why does the timer() function work perfectly in chrome and firefox, and yet the seconds reset in Opera at 8 seconds? If I don't try to beautify the seconds and use the commented out line instead, it works perfectly.
Best regards!
var timerOn = false;
function drag(e) {
if (timerOn == false) {
timerOn = window.setInterval(function(){ timer() }, 1000);
}
...
}
function timer() {
var content = document.getElementById("timer").textContent.split(":");
if (parseInt(content[1]) == 59) {
content[0] = (parseInt(content[0]) + 1).toString();
content[1] = "00";
}
else {
var s = parseInt(content[1]) + 1;
content[1] = (s < 10 ? "0" : "") + s.toString();
//~ content[1] = s.toString();
}
document.getElementById("timer").textContent = content[0] + ":" + content[1];
}
....
<span id="timer">0:00</span>
Because some browsers extend JavaScript's parseInt to treat the prefix 0 to mean "octal", and 08 is an invalid octal number.
In the various places you use parseInt, give it its second argument (the radix — e.g., number base — to use), e.g. parseInt(str, 10). (This is a good idea generally, for this very reason.)
I'm surprised that you're still finding this behavior in an up-to-date browser, though, as the ECMAScript5 specification released three and a half years ago explicitly forbids extending parseInt in that way, as noted in Annex E - Additions and Changes in the 5th Edition that Introduce Incompatibilities with the 3rd Edition:
15.1.2.2: The specification of the function parseInt no longer allows implementations to treat Strings beginning with a 0 character as octal values.
...and I don't think §B.1.1 - Additional Syntax - Numeric Literals applies to parseInt.
This question already has answers here:
Evaluating a string as a mathematical expression in JavaScript
(26 answers)
Calculate string value in javascript, not using eval
(12 answers)
Safe evaluation of arithmetic expressions in Javascript
(5 answers)
How to code a calculator in javascript without eval
(1 answer)
Eval alternative
(4 answers)
Closed 5 days ago.
I have a bunch of fields in a web page (150+) that need to have equations run on them to produce a result.
I currently store the equation like this:
<input name="F7" type="text" class="numeric" data-formula="([C7]-[D7])/[E7]" readonly />
When an input is blurred, I use a jQuery selector to iterate over all inputs with a data-formula attribute, take the formula, and use regex to replace the pointers (the [C7] in the equation) with their appropriate values.
After that, I eval() the equation to get a result, and put it in the correct input. This works great, but is very slow and results in the web page hanging for a few seconds, which is bad if it happens every time an input is blurred.
Is there a way to evaluate an equation, such as "(1-2)/4", without using eval()? These equations also may have functions, such as square root (which makes eval() nice, since I can just put Math.sqrt() in the formula), and the numbers may be decimals.
Note: This application must run on IE7 and 8, so I don't believe I can use Webworkers or anything like that. I have also considered only running this code after a "Save" button is hit, but I would prefer the UI to update live if possible
I only really know two alternatives, one is to use a script element that is dynamically written to the page, e.g.:
function evaluate(formula)
{
var script = document.createElement("script");
script.type = "text/javascript";
script.text = "window.__lr = " + formula + ";";
document.body.appendChild(script);
document.body.removeChild(script);
var r = window.__lr;
return r;
}
The other would be to use new Function(...):
function evaluate3(formula)
{
var func = new Function("return " + formula);
return func();
}
But I don't think you'll find something that yields similar performance to eval: http://jsperf.com/alternative-evaluation
The performance of eval varies across browsers and platforms, have you got a specific browser/platform combination in mind? The newer javascript engines in improved browsers will offer optimised eval:
This is only a limited set of tests on a few UAs, but it should give you an idea of how it performs in different environments.
Is there a way to evaluate an equation, such as "(1-2)/4", without using eval()?
Well, you can tokenize the expression and write your own evaluator that mimics what eval does. But while that might be useful in terms of limiting the side-effects (since eval is a very big hammer), it's extremely unlikely to perform better than eval does.
What you can do, though, is cache the result of evaluating all the other inputs so that you only evaluate the input the actually blurred. That should be quite efficient indeed.
For example, suppose you had this global object:
var values = {
A7: /* initial value for A7 */,
B7: /* initial value for B7 */,
C7: /* initial value for C7 */,
D7: /* initial value for D7 */,
E7: /* initial value for E7 */,
F7: /* initial value for F7 */,
/* etc */
};
...and then attached this blur handler to all inputs:
$("input").blur(function() {
values[this.id] = this.value; // Or parseInt(this.value, 10), or parseFloat(this.value), etc.
doTheEvaluation();
});
...where doTheEvaluation used the values from values rather than recalculating all of them every time.
If this.value might refer to other fields, you could do a recursive evaluation of it — but without evaluating all of your inputs.
I do realize this answer is 8 years too late, but I thought I would add my own contribution since this issue came up in a project I was working on. In my case, I am using Nodejs, but this solution should work for a browser as well.
let parens = /\(([0-9+\-*/\^ .]+)\)/ // Regex for identifying parenthetical expressions
let exp = /(\d+(?:\.\d+)?) ?\^ ?(\d+(?:\.\d+)?)/ // Regex for identifying exponentials (x ^ y)
let mul = /(\d+(?:\.\d+)?) ?\* ?(\d+(?:\.\d+)?)/ // Regex for identifying multiplication (x * y)
let div = /(\d+(?:\.\d+)?) ?\/ ?(\d+(?:\.\d+)?)/ // Regex for identifying division (x / y)
let add = /(\d+(?:\.\d+)?) ?\+ ?(\d+(?:\.\d+)?)/ // Regex for identifying addition (x + y)
let sub = /(\d+(?:\.\d+)?) ?- ?(\d+(?:\.\d+)?)/ // Regex for identifying subtraction (x - y)
/**
* Evaluates a numerical expression as a string and returns a Number
* Follows standard PEMDAS operation ordering
* #param {String} expr Numerical expression input
* #returns {Number} Result of expression
*/
function evaluate(expr)
{
if(isNaN(Number(expr)))
{
if(parens.test(expr))
{
let newExpr = expr.replace(parens, function(match, subExpr) {
return evaluate(subExpr);
});
return evaluate(newExpr);
}
else if(exp.test(expr))
{
let newExpr = expr.replace(exp, function(match, base, pow) {
return Math.pow(Number(base), Number(pow));
});
return evaluate(newExpr);
}
else if(mul.test(expr))
{
let newExpr = expr.replace(mul, function(match, a, b) {
return Number(a) * Number(b);
});
return evaluate(newExpr);
}
else if(div.test(expr))
{
let newExpr = expr.replace(div, function(match, a, b) {
if(b != 0)
return Number(a) / Number(b);
else
throw new Error('Division by zero');
});
return evaluate(newExpr);
}
else if(add.test(expr))
{
let newExpr = expr.replace(add, function(match, a, b) {
return Number(a) + Number(b);
});
return evaluate(newExpr);
}
else if(sub.test(expr))
{
let newExpr = expr.replace(sub, function(match, a, b) {
return Number(a) - Number(b);
});
return evaluate(newExpr);
}
else
{
return expr;
}
}
return Number(expr);
}
// Example usage
//console.log(evaluate("2 + 4*(30/5) - 34 + 45/2"));
In the original post, variables may be substituted using String.replace() to provide a string similar to the example usage seen in the snippet.
I would modify your code to perform only one eval.
var expressions = []
// for each field
// expressions.push("id:" + parsedExpression);
var members = expressions.join(",");
var resultObj = eval("({" + members + "})");
// for each field
document.getElementById(id).value = resultObj[id];
Validation: I'd write a powerful Regular expression to validate the input, then use eval to evaluate it if it's safe.
Evaluation: Regarding the speed of eval: If it's a big problem, you could queue up all equations (store it in an array), and evaluate them all at once:
var equations = ['1+1', '2+2', '...']; //<-- Input from your fields
var toBeEvald = '[' + equations.join(',') + '];';
var results = eval(toBeEvald);
// result[0] = 2
// result[1] = 4, etc
If you had a reliable internet connection, you could connect to google and use their services to evaluate an expression. Google has a pretty powerful server, and all you would have to do is send a request with the queue being the equation and retrieve it. Of course, this could be slower or faster depending on internet speed/browser speed.
Or, you can write your own equation evaluator. This is pretty difficult, and probably won't be any more efficient than eval. You'd also have to go through the immense trouble of the PEMDAS order.
I suggest you could merge the equations together into one string, and eval that all at once, and retrieve the results all at once.
You can use new Function to evaluate your expressions
While playing around with random numbers in JavaScript I discovered a surprising bug, presumably in the V8 JavaScript engine in Google Chrome. Consider:
// Generate a random number [1,5].
var rand5 = function() {
return parseInt(Math.random() * 5) + 1;
};
// Return a sample distribution over MAX times.
var testRand5 = function(dist, max) {
if (!dist) { dist = {}; }
if (!max) { max = 5000000; }
for (var i=0; i<max; i++) {
var r = rand5();
dist[r] = (dist[r] || 0) + 1;
}
return dist;
};
Now when I run testRand5() I get the following results (of course, differing slightly with each run, you might need to set "max" to a higher value to reveal the bug):
var d = testRand5();
d = {
1: 1002797,
2: 998803,
3: 999541,
4: 1000851,
5: 998007,
10: 1 // XXX: Math.random() returned 4.5?!
}
Interestingly, I see comparable results in node.js, leading me to believe it's not specific to Chrome. Sometimes there are different or multiple mystery values (7, 9, etc).
Can anyone explain why I might be getting the results I see? I'm guessing it has something to do with using parseInt (instead of Math.floor()) but I'm still not sure why it could happen.
The edge case occurs when you happen to generate a very small number, expressed with an exponent, like this for example 9.546056389808655e-8.
Combined with parseInt, which interprets the argument as a string, hell breaks loose. And as suggested before me, it can be solved using Math.floor.
Try it yourself with this piece of code:
var test = 9.546056389808655e-8;
console.log(test); // prints 9.546056389808655e-8
console.log(parseInt(test)); // prints 9 - oh noes!
console.log(Math.floor(test)) // prints 0 - this is better
Of course, it's a parseInt() gotcha. It converts its argument to a string first, and that can force scientific notation which will cause parseInt to do something like this:
var x = 0.000000004;
(x).toString(); // => "4e-9"
parseInt(x); // => 4
Silly me...
I would suggest changing your random number function to this:
var rand5 = function() {
return(Math.floor(Math.random() * 5) + 1);
};
This will reliably generate an integer value between 1 and 5 inclusive.
You can see your test function in action here: http://jsfiddle.net/jfriend00/FCzjF/.
In this case, parseInt isn't the best choice because it's going to convert your float to a string which can be a number of different formats (including scientific notation) and then try to parse an integer out of it. Much better to just operate on the float directly with Math.floor().
What's the "best" way to convert a number to a string (in terms of speed advantage, clarity advantage, memory advantage, etc) ?
Some examples:
String(n)
n.toString()
""+n
n+""
like this:
var foo = 45;
var bar = '' + foo;
Actually, even though I typically do it like this for simple convenience, over 1,000s of iterations it appears for raw speed there is an advantage for .toString()
See Performance tests here (not by me, but found when I went to write my own):
http://jsben.ch/#/ghQYR
Fastest based on the JSPerf test above: str = num.toString();
It should be noted that the difference in speed is not overly significant when you consider that it can do the conversion any way 1 Million times in 0.1 seconds.
Update: The speed seems to differ greatly by browser. In Chrome num + '' seems to be fastest based on this test http://jsben.ch/#/ghQYR
Update 2: Again based on my test above it should be noted that Firefox 20.0.1 executes the .toString() about 100 times slower than the '' + num sample.
In my opinion n.toString() takes the prize for its clarity, and I don't think it carries any extra overhead.
Explicit conversions are very clear to someone that's new to the language. Using type coercion, as others have suggested, leads to ambiguity if a developer is not aware of the coercion rules. Ultimately developer time is more costly than CPU time, so I'd optimize for the former at the cost of the latter. That being said, in this case the difference is likely negligible, but if not I'm sure there are some decent JavaScript compressors that will optimize this sort of thing.
So, for the above reasons I'd go with: n.toString() or String(n). String(n) is probably a better choice because it won't fail if n is null or undefined.
The below are the methods to convert an Integer to String in JS.
The methods are arranged in the decreasing order of performance.
var num = 1
Method 1:
num = `${num}`
Method 2:
num = num + ''
Method 3:
num = String(num)
Method 4:
num = num.toString()
Note: You can't directly call toString() on a number. 2.toString() will throw Uncaught SyntaxError: Invalid or unexpected token.
(The performance test results are given by #DarckBlezzer in his answer)
Other answers already covered other options, but I prefer this one:
s = `${n}`
Short, succinct, already used in many other places (if you're using a modern framework / ES version) so it's a safe bet any programmer will understand it.
Not that it (usually) matters much, but it also seems to be among the fastest compared to other methods.
...JavaScript's parser tries to parse
the dot notation on a number as a floating point literal.
2..toString(); // the second point is correctly recognized
2 .toString(); // note the space left to the dot
(2).toString(); // 2 is evaluated first
Source
Tongue-in-cheek obviously:
var harshNum = 108;
"".split.call(harshNum,"").join("");
Or in ES6 you could simply use template strings:
var harshNum = 108;
`${harshNum}`;
The simplest way to convert any variable to a string is to add an empty string to that variable.
5.41 + '' // Result: the string '5.41'
Math.PI + '' // Result: the string '3.141592653589793'
I used https://jsperf.com to create a test case for the following cases:
number + ''
`${number}`
String(number)
number.toString()
https://jsperf.com/number-string-conversion-speed-comparison
As of 24th of July, 2018 the results say that number + '' is the fastest in Chrome, in Firefox that ties with template string literals.
Both String(number), and number.toString() are around 95% slower than the fastest option.
I recommended `${expression}` because you don't need to worry about errors.
[undefined,null,NaN,true,false,"2","",3].forEach(elem=>{
console.log(`${elem}`, typeof(`${elem}`))
})
/* output
undefined string
null string
NaN string
true string
false string
2 string
string
3 string
*/
Below you can test the speed. but the order will affect the result. (in StackOverflow) you can test it on your platform.
const testCases = [
["${n}", (n) => `${n}`], // 👈
['----', undefined],
[`"" + n`, (n) => "" + n],
[`'' + n`, (n) => '' + n],
[`\`\` + n`, (n) => `` + n],
[`n + ''`, (n) => n + ''],
['----', undefined],
[`String(n)`, (n) => String(n)],
["${n}", (n) => `${n}`], // 👈
['----', undefined],
[`(n).toString()`, (n) => (n).toString()],
[`n.toString()`, (n) => n.toString()],
]
for (const [name, testFunc] of testCases) {
if (testFunc === undefined) {
console.log(name)
continue
}
console.time(name)
for (const n of [...Array(1000000).keys()]) {
testFunc(n)
}
console.timeEnd(name)
}
I'm going to re-edit this with more data when I have time to, for right now this is fine...
Test in nodejs v8.11.2: 2018/06/06
let i=0;
console.time("test1")
for(;i<10000000;i=i+1){
const string = "" + 1234;
}
console.timeEnd("test1")
i=0;
console.time("test1.1")
for(;i<10000000;i=i+1){
const string = '' + 1234;
}
console.timeEnd("test1.1")
i=0;
console.time("test1.2")
for(;i<10000000;i=i+1){
const string = `` + 1234;
}
console.timeEnd("test1.2")
i=0;
console.time("test1.3")
for(;i<10000000;i=i+1){
const string = 1234 + '';
}
console.timeEnd("test1.3")
i=0;
console.time("test2")
for(;i<10000000;i=i+1){
const string = (1234).toString();
}
console.timeEnd("test2")
i=0;
console.time("test3")
for(;i<10000000;i=i+1){
const string = String(1234);
}
console.timeEnd("test3")
i=0;
console.time("test4")
for(;i<10000000;i=i+1){
const string = `${1234}`;
}
console.timeEnd("test4")
i=0;
console.time("test5")
for(;i<10000000;i=i+1){
const string = 1234..toString();
}
console.timeEnd("test5")
i=0;
console.time("test6")
for(;i<10000000;i=i+1){
const string = 1234 .toString();
}
console.timeEnd("test6")
output
test1: 72.268ms
test1.1: 61.086ms
test1.2: 66.854ms
test1.3: 63.698ms
test2: 207.912ms
test3: 81.987ms
test4: 59.752ms
test5: 213.136ms
test6: 204.869ms
If you need to format the result to a specific number of decimal places, for example to represent currency, you need something like the toFixed() method.
number.toFixed( [digits] )
digits is the number of digits to display after the decimal place.
The only valid solution for almost all possible existing and future cases (input is number, null, undefined, Symbol, anything else) is String(x). Do not use 3 ways for simple operation, basing on value type assumptions, like "here I convert definitely number to string and here definitely boolean to string".
Explanation:
String(x) handles nulls, undefined, Symbols, [anything] and calls .toString() for objects.
'' + x calls .valueOf() on x (casting to number), throws on Symbols, can provide implementation dependent results.
x.toString() throws on nulls and undefined.
Note: String(x) will still fail on prototype-less objects like Object.create(null).
If you don't like strings like 'Hello, undefined' or want to support prototype-less objects, use the following type conversion function:
/**
* Safely casts any value to string. Null and undefined are converted to ''.
* #param {*} value
* #return {string}
*/
function string (str) {
return value == null ? '' : (typeof value === 'object' && !value.toString ? '[object]' : String(value));
}
With number literals, the dot for accessing a property must be distinguished from the decimal dot. This leaves you with the following options if you want to invoke to String() on the number literal 123:
123..toString()
123 .toString() // space before the dot 123.0.toString()
(123).toString()
I like the first two since they're easier to read. I tend to use String(n) but it is just a matter of style than anything else.
That is unless you have a line as
var n = 5;
console.log ("the number is: " + n);
which is very self explanatory
I think it depends on the situation but anyway you can use the .toString() method as it is very clear to understand.
.toString() is the built-in typecasting function, I'm no expert to that details but whenever we compare built-in type casting verse explicit methodologies, built-in workarounds always preferred.
If I had to take everything into consideration, I will suggest following
var myint = 1;
var mystring = myint + '';
/*or int to string*/
myint = myint + ''
IMHO, its the fastest way to convert to string. Correct me if I am wrong.
If you are curious as to which is the most performant check this out where I compare all the different Number -> String conversions.
Looks like 2+'' or 2+"" are the fastest.
https://jsperf.com/int-2-string
We can also use the String constructor. According to this benchmark it's the fastest way to convert a Number to String in Firefox 58 even though it's slower than
" + num in the popular browser Google Chrome.
Method toFixed() will also solves the purpose.
var n = 8.434332;
n.toFixed(2) // 8.43
You can call Number object and then call toString().
Number.call(null, n).toString()
You may use this trick for another javascript native objects.
Just come across this recently, method 3 and 4 are not appropriate because how the strings are copied and then put together. For a small program this problem is insignificant, but for any real web application this action where we have to deal with frequency string manipulations can affects the performance and readability.
Here is the link the read.
It seems similar results when using node.js. I ran this script:
let bar;
let foo = ["45","foo"];
console.time('string concat testing');
for (let i = 0; i < 10000000; i++) {
bar = "" + foo;
}
console.timeEnd('string concat testing');
console.time("string obj testing");
for (let i = 0; i < 10000000; i++) {
bar = String(foo);
}
console.timeEnd("string obj testing");
console.time("string both");
for (let i = 0; i < 10000000; i++) {
bar = "" + foo + "";
}
console.timeEnd("string both");
and got the following results:
❯ node testing.js
string concat testing: 2802.542ms
string obj testing: 3374.530ms
string both: 2660.023ms
Similar times each time I ran it.
Just use template literal syntax:
`${this.num}`
when i try "eval" function as eval ("020 * 05 + 05") it is returning 85 instead off 105.
Can someone explain me why eval function behave like this? Also suggest any to overcome this problem.
Numeric constants that start with a zero (like "020") are interpreted as octal. That's true for C, C++, Java, Javascript, and most any other language with even a vague cosmetic relationship to C.
If for some reason you really, really need to use "eval()", and you've got these weird strings with bogus leading zeros on the numeric constants, you might try something like this:
var answer = eval(weirdString.replace(/\b0(\d+)\b/g, '$1'));
However I wish you would find a way around using "eval()" at all. (Note the comment below noting that the hack shown above will have problems with numbers containing fractional parts.)
Javascript treats numbers beginning with 0 as octal. You can either remove the leading 0's or use parseInt(yourNumber,10) to convert to base 10.
Here is a link describing how the ParseInt function works in JavaScript and hence the reason you are getting an unexpected result.
http://www.w3schools.com/jsref/jsref_parseInt.asp
Here's a cleaner, safer answer, assuming you've first ensured the expression doesn't have any illegal characters (in particular any lowercase z's) in it:
"blah blah arithmetic with leading 0's... 0012.0034 + 1200 - 05.0600*0 + 0"
.replace(/\b0+\b/g, 'z') // replace bare zeros with sentinel
.replace(/[1-9\.]0+/g, m => m.replace(/0/g, 'z')) // save these too
.replace(/0/g, '') // throw away the rest of the zeros
.replace(/z/g, '0') // turn sentinels back to zeros
HT Adam Wolf for reminding me of the term sentinel.
"100.0001".replace(/\b0(\d+)\b/g, '$1') ="100.1" so it dangerous solution
My solution:
function $calc(n, round, min, max) {
/// <summary>calculate expression from string</summary>
/// <param name="round" type="int">optional</param>
/// <param name="min" type="int">optional. minimum allowed value. if less return 0</param>
/// <param name="max" type="int">optional. maximum allowed value. if more return 0</param>
if (!n) return 0;
try {
n = Number(eval(n
.replace(/[^\d\.\-\+\*\/\(\)\e]/g, '')//remove illegal symbols
.replace(/^0+/, '')//replace first leading zero
.replace(/[^\d\.]+0+/g, function (s) {return s.substr(0, 1);}) //replace leading zero
));
} catch (e) { return 0; }
if (n == 0 || !isFinite(n)) return 0;
if (round != undefined) { var t = Math.pow(10, round); n = Math.round(n * t) / t; }
if (min != undefined && n < min) return 0;
if (max != undefined && n > max) return 0;
return n;
}
function is safe. if calculation is fail or NaN of infinite, return 0
$calc('0100.08-(0.01+00.04)')=100.03
$calc('0/0')=0 //NaN
$calc('1/3',2)=0.33 //round