Is there an eval() alternative for this expression? - javascript

I have a string expression like such: "1+2+3", and it must stay as a string. In other words, looping and casting the digits to perform the operation isn't an option, so my solution is eval("1+2+3"). But eval() is slow and has all these issues associated with it. Is there an alternative to evaluate my string?

Evaluating a string is not only slow, it's dangerous. What if, by malicious user intent or error, you end up evaluating code that crashes your program, destroys your data o opens a security hole?
No, you should not eval() the string. You should split it, cast the operands to numbers, and sum them.
You can keep the string around if you like (you said you needed the string), but using the string to actually perform this operation is a Really Bad Idea.
var string = "1+2+3"
var numbers = string.split('+').map(function(x) { return parseInt(x) })
var sum = numbers.reduce(function(total, x) { return total + x }, 0)

This is a silly question:
var reducer = function (a, b) {
return +a + +b;
};
"1+2+3".match(/[+-]?\d+/g).reduce(reducer); // 6
// or addition only
"1+2+3".split(/\D/).reduce(reducer); // 6

Related

Is it safe to use strings in arithmetic expressions?

I have a number of seconds in a string, like: '5'.
From that I need to get the number of milliseconds and it has to be of type Number, like: 5000.
I know that you can easily convert strings to numbers by prefixing them with a +
const result = +'5';
console.log(result, typeof(result));
However playing around I saw that that's not even necessary because JavaScript automatically does the conversion for you when you try to use arithmetic between strings and numbers.
const result = '5' * 3;
console.log(result, typeof(result));
console.log('5.3' * 3);
On the docs I only found info about the Number() constructor.
My question is: is it safe to use arithmetic on strings (except for the addition)? Can I rely on the behaviour showed above?
Yes, it is safe. All arithmetic operations except a binary + will convert the operands to numbers. That includes bitwise operators as well as unary plus.
With that said, it is probably a good idea not to rely on this extensively. Imagine that you have this code:
function calculate(a, b) {
return a * 2 + b * 3;
}
//elsewhere in the code
console.log(calculate("5", "2"));
This works fine because both a and b are multiplied, so are going to be converted to numbers. But in six months time you come back to the project and realise you want to modify the calculation, so you change the function:
function calculate(a, b) {
return a + b * 3;
}
//elsewhere in the code
console.log(calculate("5", "2"));
...and suddenly the result is wrong.
It is therefore better if you explicitly convert the values to numbers if you want to do arithmetic. Saves the occasional accidental bug and it is more maintainable.
Yes, but you have to be careful...
console.log('5.3' * 3);
console.log('5.3' + 3);
These two very similar functions cast the values different ways:
* can only be applied between two numbers, so '5.3' becomes 5.3
+ can also concatenate strings, and the string comes first, so 3 becomes '3'
If you understand all these you can do this, but I'd recommend against it. It's very easy to miss and JS has a lot of weird unexpected casts.

Convert "10/2" to a number? [duplicate]

This question already has answers here:
How to evaluate a math expression given in string form?
(26 answers)
Closed 1 year ago.
I am working on a discord.js and have a found the following problem, I want to convert the users input into a number I am working on a calc bot so the arguments were like 10/2 but I couldn't find a method of converting the string into a number so I thought i would ask, I thought maybe the Number function could work but it didn't and tried using arrays but the join function simply converts it to a string. Anyone know how to solve this?
If you want to avoid the use of eval, you need to parse out the numbers, convert them to numbers, and perform the appropriate operation.
const rx = /(\d+(?:\.\d+)?)\s*([+\-\*\/%])\s*(\d+(?:\.\d+)?)/;
function math(str) {
const [full, lhs, op, rhs] = rx.exec(str);
let retval
switch (op) {
case '+':
retval = Number(lhs) + Number(rhs);
break;
// etc...
}
return retval;
}
console.log("1 + 1 = ", math("1 + 1"));
console.log("1.1 + 1.1 = ", math("1.1+1.1"));
Note that the code above doesn't have any error checking to bail if the string provided isn't a valid operation, or if the numbers aren't really numbers. It's only an example of how you can use a regular expression to get these values, and avoid using the potentially dangerous eval.
The easiest way to do this is by using eval() which takes a string containing javascript code, evaluates it, and returns the result.
WARNING: this is very dangerous and you can send any javascript code with it and javascript will happily execute it.
This would give users of the bot the ability to do any command and basically take remote control of your computer/server.
To protect yourself from this you should make sure that the string only contains some allowed characters like this:
const allowedChars = "1234567890/*+-% ";
const input = "2323 + 323";
if (![...input].some(x => !allowedChars.includes(x))) {
// safe to evaluate
const result = eval(input);
} else {
// not safe to execute
}

Should literal numbers not have quotes?

I know that literal numbers do not require quotes around the value. For instance, var x=123; is acceptable and does not need to be var x='123'; or var x="123";.
That being said, is there anything wrong with quoting a literal number?
If the "number" was a zipcode or database record ID, and not a number in the normal sense which might be used in arithmetic, would the answer be different?
It isn't a number. Quoting a number makes it a string, which can make for some differences in the way they're handled. For example:
var a = 1;
var b = '33';
console.log(a + b === 34); // false
console.log(a + b === '34'); // true
Strings also have different types and methods for manipulating them. However, for most of the numeric operators (-, /, *, and other bitwise operators), they convert the string form to its numeric equivalent before performing the operation.
There are also a few differences where numbers are not stored with their exact value in some cases, due to the nature of the floating point format JavaScript numbers are stored in. Strings avoid this problem, though it is much harder to manipulate them. Converting these back to numbers reintroduces these issues. For example, see this:
var recordID = 9007199254740992;
var previousID = recordID;
recordID += 1;
console.log(recordID === previousID); // true
Adding quotes makes the number a string literal and so serves a different purpose than the Number literal defined without quotes.
JavaScript has the concept of type coercion which might have confused you.
Quoting makes a string of a number. It means that for example + operation will concatenate instead of add:
var a = 'asdf';
var b = '20';
var c = a + b; // asdf20
Here is a great explanation of what is going on.
I know that literal numbers do not require quotes around the value. For instance, var x=123; is acceptable and does not need to be var x='123'; or var x="123";.
It's not a matter of required Vs not required (optional)
Using quotes (single or double) you state that it is a string (a sequence of characters - no matter if they're all digits)
If you don't place quotes you state it is a number.
That being said, is there anything wrong with quoting a literal number?
No if the entity it represents is not actually a number but a string. So...
If the "number" was a zipcode or database record ID, and not a number in the normal sense which might be used in arithmetic, would the answer be different?
If the number is a zipcode it may make sense to put quotes, because it is a "code", not a number and is not subject to arithmetics operations.
You're not going to divide a zipcode by 2 or sum two zipcodes because that would not make sense.
But instead of deciding to use quotes or not based on what the value represents I suggest you to consider the problem from the language perspective
You should understand and keep always in mind how do the language's operators behave when you use a string instead of a number in an expression (assignment or comparison).

What is the JSLint approved way to convert a number to a string?

I've always converted numbers to strings by adding an empty string to them:
var string = 1 + '';
However, JSLint complains of this method with Expected 'String' and instead saw ''''., and it does look a little ugly.
Is there a better way?
I believe that the JSLint approved way is to call .toString() on the number:
var stringified = 1..toString();
// Note the use of the double .. to ensure the the interpreter knows
// that we are calling the toString method on a number --
// not courting a syntax error.
// You could also theoretically call 1["toString"];
(Sorry, it possibly would've been better to say this as a comment above but I haven't yet earned the right to post comments, so...)
Remember that jslint is not just validating whether your JavaScript will actually run, it is trying to enforce coding style with the aim of helping you produce more readable and maintainable code.
So 1 + '' works, but isn't necessarily the most readable option for everybody while explicit casting options (see the other answers) should be readable for everybody. Of course if nobody else will ever see your code you need only worry about whether you will be able to understand it if you come back to it next month, or next year...
Don't forget that the following two statements don't produce the same result:
var s1 = 1 + 3 + ''; // gives '4'
var s2 = '' + 1 + 3; // gives '13'
I assume 1 + '' is just a simplification for discussion though, or why not just use '1' in the first place?
You can use the .toString() method like so:
var num = 1;
var str = num.toString();
There's also (at least in Chrome): String(1) without new.
var n = 1, s = String(n);
I am going to say "bug" or "mis-feature"
Cole Consider that
var x = "foobar" + 1;
is "approved" jslint. In any case, it is 100% valid Javascript.
Happy coding.
For comment:
This is why I prefer to use the string literal as the first operand as this shows intent -- knowing a language is fundamental to using a language.
The only place the duck-typing is an issues (in this case) is with an expression of the form a + b, where neither is a string literal. In this case a (or b) may evaluate to a string where it was expected to evaluate to a number (this would trigger the string concatenation vs. the expected numeric addition). If any of the operands are a string literal the intent is well-defined/described.
This particular "issue", however, is not present in the posted code; nor would it be eliminated with the use of toString over a string literal.
In my opinion, we should use String(number) instead of number + '' or number.toString(), because
number + '' will trigger the JSLint error
number.toString() will fail in case number is null or undefined, and you will have TypeError: number is undefined / null
Recently, JSLint was in beta. The new version no longer complains about this code:
function convert(x) {
'use strict';
// alert(typeof x);
x = x + "";
// alert(typeof x);
return x;
}
convert(3);

why do I get 24 when adding 2 + 4 in javascript

I am trying this:
function add_things() {
var first = '2';
var second = '4';
alert(first + second);
}
But it gives me 24 instead of 6, what am I doing wrong?
You're concatenating two strings with the + operator. Try either:
function add_things() {
var first = 2;
var second = 4;
alert(first + second);
}
or
function add_things() {
var first = '2';
var second = '4';
alert(parseInt(first, 10) + parseInt(second, 10));
}
or
function add_things() {
var first = '2';
var second = '4';
alert(Number(first) + Number(second));
}
Note: the second is only really appropriate if you're getting strings from say a property or user input. If they're constants you're defining and you want to add them then define them as integers (as in the first example).
Also, as pointed out, octal is evil. parseInt('010') will actually come out as the number 8 (10 in octal is 8), hence specifying the radix of 10 is a good idea.
Try this:
function add_things() {
var first = 2;
var second = 4;
alert(first + second);
}
Note that I've removed the single quotes; first and second are now integers. In your original, they are strings (text).
That is one of the "Bad Parts" of JavaScript, as a loosely typed language, the addition and concatenation operator is overloaded.
JavaScript is loosely typed, but that doesn't mean that it has no data types just because a value of a variable, object properties, functions or parameters don't need to have a particular type of value assigned to it.
Basically there are three primitive data types:
boolean
number
string
null and undefined are two special cases, everything else are just variations of the object type.
JavaScript type-converts values of types into a type suitable for the context of their use (type coercion).
In your example were trying to add two objects of type string, so a concatenation occur.
You can "cast" or type convert the variables to number in many ways to avoid this problem:
var a = "2";
var b = "4";
// a and b are strings!
var sum = Number(a) + Number(b); // Number constructor.
sum = +a + +b; // Unary plus.
sum = parseInt(a, 10) + parseInt(b, 10); // parseInt.
sum = parseFloat(a) + parseFloat(b); // parseFloat.
This is I think a very common mistake, for example when reading user input from form elements, the value property of form controls is string, even if the character sequence that it contain represents a number (as in your example).
The "Bad Part" which I talk, is about the dual functionality of the + operator, overloaded to be used for both, numeric addition and string concatenation.
The operation that the + operator will do is determined completely by the context. Only if the both operands are numbers, the + operator perform addition, otherwise it will convert all of its operands to string and do concatenation.
The single quotes cause the values to be treated as characters instead of numbers. '2' + '4' = '24' in the same way that 'snarf' + 'blam' = 'snarfblam'.
You could also force the interpreter to perform arithmetic when dealing with numbers in string forms by multiplying the string by 1 (since multiplication can't be done on a string, it'll convert to a number if it can):
// fun with Javascript...
alert(first * 1 + second * 1);
But it's probably best to go with CMS's suggestion of using Number() to force the conversion, since someone will probably come along later and optimize the expression by removing the 'apparently unnecessary' multiply-by-one operations.

Categories

Resources