parsing a number comparison string in javascript - javascript

So I'm wondering if there is a simple method for, i guess, parsing a string that contains a number comparison.
For example I have a variable that contains a string like this
str = "1>3";
Right now I'm using the math plugin to use math.eval(str) which returns a boolean but I feel like this is an awfully big resource to include just for a simple comparison.
My script is currently like this, and I love the simplicity I just hate having to use such a big resource for it.
if (math.eval(str) == true) {
//do something
} else {
// do something else
}
Any input on this matter would be greatly appreciated. I thank you in advance.

If you need that for friendly environment (non-public, no security considerations) you can use regular eval to compute result:
var result = eval("1>3");
Note that eval let you run any code and if you let user enter the "condition" and plan to use that for anything outside of personal calculator sample on you local machine - use proper parser that validates expressions and does not use arbitrary functions.

If you have such simple expressions, like
expression := number comparison_operator number
comparison_operator := < | > | = | ..
then it is also very simple to write a parser for this.
E.g. split the input string along the operator,
"1>3" ->
op = ">" ->
split: ["1", "3"] ->
num1_str = "1", num2_str = "3"
convert each number from its string representation to a number value.
num1 = 1, num2 = 3
Then apply the comparison operator.
switch (op) {
case "<":
return num1 < num2;
case ">":
return num1 > num2;
case "=":
return num1 == num2;
// <=, >=, !=, ..
default:
// unknown operator
}

This lib does a lot of things out of the box for expression parsing.

Related

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
}

Why is this simple JavaScript XOR encryption algorithm not working?

I am trying to develop a (very) simple XOR encryption algorithm in JavaScript.
In PHP, the following encryption algorithm works as expected:
function encryptXor($text, $key) {
$result = '';
for ($i = 0; $i < strlen($text); $i++)
$result[$i] = $text[$i] ^ $key[$i % strlen($key)];
return $result;
}
However, the following JavaScript algorithm does not work properly:
function encryptXor(text, key) {
var result = '';
for(var i = 0; i < text.length; i++)
result += text.charAt(i) ^ key.charAt(i % key.length);
return result;
}
A test case follows:
Text: someRandomText
Key: 123456789
PHP output: B]^QgWY\V\fVLA
JS output: 12345678912345
Apparently, the ^ operator is behaving differently between the two languages.
I have found some questions regarding the difference between PHP's and JavaScript's ^ operators, such as this one, this one or this one, but I still could not resolve this issue.
Why is this JavaScript algorithm not working as expected?
Minor note 1: Since it is not possible to iterate over the characters of a string and replace them one by one in JavaScript, I used the += operator inside the for loop in order to append the output of each XOR operation to the result variable.
Minor note 2: In order to normalize the character set, the functions actually return base64_encode($result) and btoa(result) in PHP and JS, respectively. Then, in order to decrypt it, the decryptXor() functions have to decode the result variables before iterating over them. Nonetheless, since this is not relevant to the question, I simplified the functions a bit. In this case, for the purpuses of testing, it is safer to use only alphanumeric characters in the text variable and only numerals in the key variable (or vice-versa).
You need some other methods, like String#charCodeAt or String.fromCharCode for getting the same result.
The main problem is, you need a numerical value instead of the character/string.
function encryptXor(text, key) {
var result = '';
for (var i = 0; i < text.length; i++) {
result += String.fromCharCode(text.charCodeAt(i) ^ key.charCodeAt(i % key.length));
}
return result;
}
console.log(encryptXor('someRandomText', '123456789')); // B]^QgWY\V\fVLA
A bit shorter version
function encryptXor(text, key) {
return Array.from(
text,
(c, i) => String.fromCharCode(c.charCodeAt() ^ key.charCodeAt(i % key.length))
).join('');
}
console.log(encryptXor('someRandomText', '123456789')); // B]^QgWY\V\fVLA
Your main problem is that you are using charAt instead of charCodeAt. 'someRandomText'.charAt(0) evaluates to 's'. 's' ^ x === x, since the ^ operator attempts to convert both operands to numbers and Number('s') is NaN. So when you xor with any character other than a numeric one you just get the value of the key.
You also have another problem regarding Note 1: You can't just append to a string. If your algorithm results in the string '567' it's impossible to know whether that was created from concatenating '56' and '7' or '5' and '67'. That becomes a much bigger problem as the string gets longer. You could use an array instead of a string or left zero pad each substring to the same length or as Nina suggested, you could convert the result of the XOR back to a single char with String.fromCharCode.

Is there an eval() alternative for this expression?

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

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