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
Related
Whats the best approach with javascript to evaluate an expression like 3+1*sin(20)*e^3?
First of all, I do not want to use built-in math-functions. In fact, I have already defined my own functions for power, square root, sin, cos, exponential, etc.
What I'm looking for, is a good way to parse an expression so I can pass them to these functions I created. I know there is an eval() function available in JavaScript for evaluating input expressions, but I'm not sure if that's suitable for this context.
Should I use eval()? If yes, how do I use it in this context? If no, what's a more suitable way to parse mathematical expressions?!
It's not an school assignment, its just a doubt.
Using a third party library
In a real life situation, you'd want to use a library like math.js to do the calculations for you.
To use this library, first include it in your HTML :
<script src='path/to/math.js'></script>
Then, you can just use the math.eval method to evaluate your expression :
math.eval('3+1*sin(20)*e^3');
That is, if 20 is a value expressed in radians. As it's probably a value expressed in degrees, you'd need to make this small adjustment :
math.eval('3+1*sin(20 deg)*e^3');
Because you explicitly said that you do not want to use the built-in math-functions, I suspect you're working on a school assignment. Note that submitting school assignments on StackOverflow is frowned upon, as it is considered a form of cheating.
But let's just ignore this and move on, shall we?!
Doing it on your own
Step 1 : Calculation with built-in math functions
In real life projects you should ALWAYS use built-in math functions or third party libraries (like math.js) that use built-in math functions under the hood. That's because built-in math functions are both 100% reliable and performance optimized.
It's impossible to create your own equivalent of built-in math functions in a way that is both as reliable and as performance optimized as the built-in functions, so there is NEVER a good reason NOT to use the built-in functions. But since you explicitly asked for it, let's ignore that and look at how to do things WITHOUT the built-in math functions. I assume this also implied you do not want to use math.js or any other library.
Using JavaScript's built-in functions, you'd calculate your expression like this :
3+1*Math.sin(20)*Math.pow(Math.E,3)
Built-in functions used :
Math.sin(x) can be used to calculate the sin of x
Math.pow(x, n) can be used to calculate xⁿ
Math.E() is a constant that represents e
Here, again, it's important to state this is true only if 20 is a value in radians. As 20 is probably a value in degrees, you probably want to define your own degree based sin function like this :
var dsin = function() {
var piRatio = Math.PI / 180;
return function dsin(degrees) {
return Math.sin(degrees * piRatio);
};
}();
Now, replace Math.sin with dsin
3+1*dsin(20)*Math.pow(Math.E,3)
In any real world application, this would be the best was to calculate 3+1*sin(20)*e^3!
Step 2 : Replacting built-in math functions with your own
Now, as you REALLY, REALLY, REALLY seem to want to go without built-in functions, just the next step is to replace every built-in function with a self-written equivalent. Often, there are multiple mathematical paths towards the same goal.
For example, you could write your pow function recursively like this :
var pow = function(base, exp) {
if (exp == 0)
return 1;
else
return base * power(base, exp - 1);
}
An iterative implementation :
var pow = function(base, exp) {
var result = 1;
while(exp--) {
result *= base;
}
return result;
}
So now what, you ask?! How can you implement dsin without relying on Math.sin, you ask?!
Well... A little bit of research tells you that you can calculare sin, using the formula sin(x) = x - x^3 / 3! + x^5 / 5! - x^7 / 7! + x^9 / 9! - ...
In JavaScript, that would be something like this :
var sin = function(x) {
var t1 = -1,t2 = 3,v1 = x,v2 = v1 + 1,it = 0;
while (it < 10) {
v2 = v1 + (t1 * pow(x, t2) / function(n){
j = 1;
for(i=1;i<=n;i++)
j = j*i;
return j;
}(t2));
t1 = -1 * t1;
t2 += 2;
v1 = v2;
it++;
}
return v2;
}
You might notice that I put my factorial calculation in a lambda function. A more elegant solution would be to put your lambda function in a separate function, as you're likely to need it elsewhere.
Now, you just need to puzzle all the pieces together and wrap them in a nice object :
var Calc = (function(){
return {
E : 2.718281828459045,
pow : function (base, exp) {
var result = 1;
while (exp--) {
result *= base;
}
return result;
},
fact : function(n){
j = 1;
for(i=1;i<=n;i++)
j = j*i;
return j;
},
sin : function(x) {
var t1 = -1,t2 = 3,v1 = x,v2 = v1 + 1,it = 0;
while (it < 10) {
v2 = v1 + (t1 * this.pow(x, t2) / this.fact(t2));
t1 = -1 * t1;
t2 += 2;
v1 = v2;
it++;
}
return v2;
},
dsin : function() {
var piRatio = Math.PI / 180;
return function dsin(degrees) {
return Math.sin(degrees * piRatio);
};
}()
};
})();
You can use the methods of these objects like this :
3+1*Calc.dsin(20)*Calc.pow(Calc.E,3)
Step 3 : Parsing
If you actually want your application to read an expression like 3+1*sin(20)*e^3 directly, you should NOT use eval(). Instead, you should write your own parser.
The proper way to do this, is to first write a tokenizer that converts your expression to a syntax tree. Then, you pass your syntax tree to an interpreter that processes your syntax tree by running the calculation functions you've created in step 1.
For an in depth look into how to build a syntax tree and interpreter, you could take a look at this tutorial.
I wrote a parser that does what you need. It's called SwanJS and it doesn't use eval or new Function.
Installation:
npm install #onlabsorg/swan-js
Usage:
const swan = require('#onlabsorg/swan-js'); // It works also in the browser
const evaluate = swan.parse("3 + 1 * sin(20) * e**3");
const context = swan.createContext({
sin: Math.sin, // or your own sin function
e: Math.E
});
const value = await evaluate(context);
If you don't like ** as power operator, you may change it, but that's not well documented. Look at /lib/interpreter.js to see how to define a new grammar or ask me if you prefer.
If you want more mathematical functionalities in addition to the standard JavaScript math library, then you may want to use a third party library like math.js.
To use this library, first include it in your HTML :
<script src='path/to/math.js'></script>
Then, you can just use the math.eval method to evaluate your expression :
math.eval('3+1*sin(20)*e^3');
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().
I have some simple variables whose values are numeric strings:
var s = '10*10*10',
v = '60*10';
I want to turn s into 1000 and v to 600.
Use eval() function:
var result = eval('10*10*10');
alert(result); // alerts 1000
If the strings are from a truly trusted source, you can use eval to do that:
var s = '10*10*10';
var result = eval(s);
But note that eval fires up a JavaScript parser, parses the given string, and executes it. If there's any chance that the given string may not be from a trusted source, you don't want to use it, as you're giving the source the ability to arbitrarily execute code.
If you can't trust the source, then you'll have to parse the string yourself. Your specific examples are easy, but I'm sure your actual need is more complex.
The dead easy bit:
var s, operands, result, index;
s = '10*10*10';
operands = s.split('*');
result = parseInt(operands[0], 10);
for (index = 1; index < operands.length; ++index) {
result *= parseInt(operands[index], 10);
}
...but again, I'm sure your actual requirement is more complex — other operators, spaces around the values, parentheses, etc.
Picking up on Andy E's comment below, whitelisting might well be a way to go:
function doTheMath(s) {
if (!/^[0-9.+\-*\/%() ]+$/.test(s)) {
throw "Invalid input";
}
return eval('(' + s + ')');
}
var result = doTheMath('10*10*10'); // 1000
var result2 = doTheMath('myEvilFunctionCall();'); // Throws exception
Live example
That regexp may not be perfect, I'd stare at it long and hard before I'd let any unwashed input head its way...
this could be achieved quite simply without resorting to eval
function calc(s) {
s = s.replace(/(\d+)([*/])(\d+)/g, function() {
switch(arguments[2]) {
case '*': return arguments[1] * arguments[3];
case '/': return arguments[1] / arguments[3];
}
})
s = s.replace(/(\d+)([+-])(\d+)/g, function() {
switch(arguments[2]) {
case '+': return parseInt(arguments[1]) + parseInt(arguments[3]);
case '-': return arguments[1] - arguments[3];
}
})
return parseInt(s);
}
alert(calc("10+5*4"))
You can use the eval function to evaluate an expression in a string:
var evaluated = eval(s);
alert(evaluated) will then alert 1000.
If you "just" want to have these numbers out of the string you can do
eval(s)
to get "10*10*10" as a Number
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}`
Is there a way to resolve mathematical expressions in strings in javascript? For example, suppose I want to produce the string "Tom has 2 apples, Lucy has 3 apples. Together they have 5 apples" but I want to be able to substitute in the variables. I can do this with a string replacement:
string = "Tom has X apples, Lucy has Y apples. Together they have Z apples";
string2 = string.replace(/X/, '2').replace(/Y/, '3').replace(/Z/, '5');
However, it would be better if, instead of having a variable Z, I could use X+Y. Now, I could also do a string replace for X+Y and replace it with the correct value, but that would become messy when trying to deal with all the possible in-string calculations I might want to do. I suppose I'm looking for a way to achieve this:
string = "Something [X], something [Y]. Something [(X+Y^2)/(5*X)]";
And for the [___] parts to be understood as expressions to be resolved before substituting back into the string.
Thanks for your help.
There's no direct, built-in way (well, okay, perhaps there is — see below), but if you use the callback feature of the replace function, where the replacement can be a function rather than a string (the return value is what's substituted in), you can implement this fairly easily.
For instance, suppose you use the Ruby notation #{xyz} for your placeholders. This code loops through those:
var mappings, str;
str = "One #{X} three #{Y} five";
mappings = {
"X": 2,
"Y": 4
};
str = str.replace(/\#\{([^#]+)\}/g, function(match, key) {
var result;
result = mappings[key];
/* ...processing here */
return result;
});
The resulting string is One 2 three 4 five, because #{X} and #{Y} have been replaced via lookup. You can look at the key and see whether it's an expression and needs to be evaluated rather than simply looked up. That evaluation is where your real work comes in.
Now, you could use with and eval to achieve expression support; change the result = mapping[key]; line above to this:
with (mappings) {
result = eval(key);
}
If you feed the string "One #{X} three #{Y} five #{X + Y * 2}" into that, the result is One 2 three 4 five 10 — because 2 + 4 * 2 = 10.
That works because with sticks the given object on top of the scope chain, so it's the first thing checked when resolving an unqualified reference (like X), and eval executes Javascript code — and so can evaluate expressions — and magically does so within the scope in which it's called. But beware; as Eric pointed out, not all operators are the same in various forms of expression, and in particular Javascript interprets ^ to mean "bitwise XOR", not "to the power of". (It doesn't have an exponent operator; you have to use Math.pow.)
But you need to be very careful about that sort of thing, both with and eval (each in their own way) can be problematic. But the main issues with with are that it's hard to tell where something comes from or where it will go if you do an assignment, which you're not; and the main issues with eval come from using it to interpret strings you don't control. As long as you keep safeguards in place and are aware of the issues...
Boiling that down into a function:
function evaluate(str, mappings) {
return str.replace(/\#\{([^#]+)\}/g, function(match, key) {
var result;
with (mappings) {
result = eval(key);
}
return result;
});
}
alert(evaluate(
"The expression '(#{X} + #{Y}) * 2' equals '#{(X + Y) * 2}'",
{"X": 2, "Y": 4}
)); // alerts "The expression '(2 + 4) * 2' equals '12'"
alert(evaluate(
"The expression '(#{X} + #{Y}) * 2' equals '#{(X + Y) * 2}'",
{"X": 6, "Y": 3}
)); // alerts "The expression '(6 + 3) * 2' equals '18'"
The only way I can think of to achieve this would be a templating engine such as jTemplates. Also see the answers to this SO question.
Nice question:
function substitutestring(str,vals)
{
var regex = /\[[^\]]*\]/gi;
var matches = str.match(regex);
var processed = [];
for(var i = 0; i<matches.length; i++)
{
var match = matches[i];
processed[match] = match.slice(1,-1);
for(j in vals)
{
processed[match] = processed[match].replace(j,vals[j]);
}
processed[match] = eval("("+processed[match]+")");
}
for(var original in processed)
{
str = str.replace(original,processed[original]);
}
return str;
}
document.write(
substitutestring(
"[x] + [y] = [x+y]",
{"x": 1, "y": 2}
)
);
In ES6 you can now use template strings:
var X = 2, Y = 3;
string = Tom has ${X} apples, Lucy has ${Y} apples. Together they have ${X+Y} apples;