Javascript evaluate string as a comparison operator - javascript

I was wondering if it would be possible to compare an int "1" and a string "!0", and make the outcome true.
For example:
//Comparing 0 and "0" works...
var myVariable = 0;
var checkVariable = "0";
if(myVariable == checkVariable)
{
console.log('Works');
}
//Comparing 1 and "!0" doesn't work:
var myVariable = 1;
var checkVariable = "!0";
if(myVariable == checkVariable)
{
//I would LIKE this to be true!
console.log('This part doesn't work');
}
Any ideas on how to accomplish this?
I am open to suggestions, and "!=0" would also be fine as well.
//////////////////////////////////////////////////
Update:
So I'm trying eval now, and here are the results:
var myVariable = 20;
var checkVariable = "20";
eval(myVariable,checkVariable);
//Returns "20", which is ok I guess, but it would be nice if it returned "true"
var myVariable = 21;
var checkVariable = "!20";
eval(myVariable,checkVariable);
//Returns "21", which is ok, but it would be nice if it returned "true"
var myVariable = 21;
var checkVariable = "20";
eval(myVariable,checkVariable);
//Returns "20", which is *wrong*
Also tried this in chrome's javascript console (#Ishita):
var myVariable = 21;
var checkVariable = "!20";
myVariable == eval(checkVariable);
//Returns "false", which should be "true" :/

Don't use eval it's widely considered to be one of the worst parts of Javascript and isn't needed at all in this case. (see bottom of this answer for more info on that)
Something like this would be an appropriate solution:
JSFiddle: http://jsfiddle.net/CoryDanielson/zQjyz/
First, setup a map of checkVariables and functions that implement the expected comparison. All these functions do is accept a number and return true/false based on the result of a comparison.
var checkFunctions = {
"0": function(num) { return parseFloat(num) === 0 },
"!0": function(num) { return parseFloat(num) !== 0; }
};
Next, modify your if statements to fetch the proper checkFunction based on the checkVariable and pass the myVariable into that function.
//Comparing 0 and "0" works...
var myVariable = 0;
var checkVariable = "0";
if ( checkFunctions[checkVariable](myVariable) )
{
console.log(myVariable + " equals zero");
}
//Comparing 1 and "!0" doesn't work:
var myVariable = 1;
var checkVariable = "!0";
if ( checkFunctions[checkVariable](myVariable) )
{
console.log(myVariable + " does not equal zero.");
}
Don't use eval needlessly!
eval() is a dangerous function, which executes the code it's passed
with the privileges of the caller. If you run eval() with a string
that could be affected by a malicious party, you may end up running
malicious code on the user's machine with the permissions of your
webpage / extension. More importantly, third party code can see the
scope in which eval() was invoked, which can lead to possible attacks
in ways of which the similar Function is not susceptible.
eval() is also generally slower than the alternatives, since it has to
invoke the JS interpreter, while many other constructs are optimized
by modern JS engines.
There are safe (and fast!) alternatives to eval() for common
use-cases.

You can use "eval" function to accomplish what you want.

This works:
var a = 1;
var b = "!0";
if(a == eval(b))
{
console.log(true);
} else {
console.log(false);
}

Parse the string into an integer using parseInt
if(parseInt("1")!=parseInt("0"))
{
console.log('Try this');
}

Related

Javascript if statement in function overwriting global variable?

Am attempting to create a static navigation panel which becomes absolute at the bottom before the footer when reaching the end of the page content.
As I am developing for wordpress the page could be of varying height so I have attempted to trigger the absolute positioning when the nav panel “collides” with the footer.
So far I have used this code I found here
function collision($archive, $footer){
var archivexPos = $archive.offset().left;
var archiveyPos = $archive.offset().top;
var archiveHeight = $archive.outerHeight(true);
var archiveWidth = $archive.outerWidth(true);
var archiveb = archiveyPos + archiveHeight;
var archiver = archivexPos + archiveWidth;
var footerxPos = $footer.offset().left;
var footeryPos = $footer.offset().top;
var footerHeight = $footer.outerHeight(true);
var footerWidth = $footer.outerWidth(true);
var footerb = footeryPos + footerHeight;
var footerr = footerxPos + footerWidth;
if (archiveb < footeryPos || archiveyPos > footerb || archiver < footerxPos || archivexPos > footer) return Boolean = false;
return Boolean = true;
And used a global variable of Boolean to pass to this function
$(window).on('scroll', function() {
var scrollmath = Math.round($(window).scrollTop());
var archiveValue = scrollmath + 48;
var archiveBottom = archiveValue + 'px';
console.log('collision boolean', Boolean)
if (Boolean = false) {
$('#archive').css('position', 'fixed');
$('#archive').css('top', '48px');
} else {
$('#archive').css('position', 'absolute');
$('#archive').css('top', archiveBottom);
}
My problem is the if statement seems to be creating another Boolean variable? As when I comment it out I can see that the console reports the Boolean variable as expected. However when I leave it in and they collide this happens
Whats happened here?
The primary thing that's happening is that you're using = for comparison. JavaScript uses == (or ===), not =. = is always assignment.
But when testing the value of a boolean, you don't want == or != anyway, just use the boolean directly:
if (flag) {
// It was true
} else {
// It was false
}
Or if you're just testing for false:
if (!flag) {
// flag was false
}
(Note that because JavaScript does type coercion, that will also work with variables containing values other than booleans: Any truthy value coerces to true, any falsy value coerces to false. The falsy values are 0, "", NaN, null, undefined, and of course, false; all other values are truthy.)
Separately: Boolean is not a good choice for a variable name, as it's part of the JavaScript standard library (a function).
Also, your current collision function does two things:
It sets Boolean to true or false
It returns the value it set
In general, all other things being equal, it's best if a function doesn't have side-effects like that. If the caller wants to set Boolean to the return value of the function, he/she can, there's no need for the function to do it — it's already returning the value.
And finally: Global variables are, in general something to avoid. The global namespace on browsers is incredibly crowded and it's easy to get conflicts (for instance, a global called name may well not work as expected, because there's already a name global [it's the name of the window]).
no, your real Problem is, that you overwrite the constructor for the Boolean Type.
1st. stick to coding conventions: Only classes start with an uppercase-letter.
2nd. local vars have to be declared with the var-Keyword (or let for block-scoped vars, or const).
otherwise you reference a var from a surrounding scope; and in the end, the global scope.
3rd. the equal-sign:
=== means typesafe comprison
3 === 3 //=> true
3 === '3' //=>false
== means simple comparison
3 == '3' //=> also true now
= means assignment, not comparison
var foo = 3;
if it inside of some other code like
var bar = 42 + (foo = 3);
//it works basically like
var bar = 42 + (function(){
foo = 3;
return 3; //NOT FOO!!! not even foo after the assignment
})();
//the same way, that this:
var bar = 42 + foo++;
//works basically like this:
var bar = 42 + (function(){
var result = foo;
foo = foo+1;
return result;
})();

Javascript "with" operator removal

I recently ran into some issues with a plugin and outlined the issue in this post: With operator & dashes in object keys and wanted to know if the modifications I've made below cover the scenarios that the with scope blocks would have covered.
I've modified some code to remove the with operator and I'm wondering if I've replicated everything properly in doing so.
Here is the original code:
var test = new Function('$f','$c','with($f){with($c){return{'+ declarations +'}}}'));
Where $f and $c are passed objects (From what I could tell, $f shouldn't ever have a property of $c). The declarations variable is a string that has a colon in it (EX: "value:color") and available within the scope.
Here is my modified code:
var test = function($f, $c, declarations) {
var result = {};
var value = "";
var split = declarations.split(":");
if (split.length < 2) {
throw new Error("Declaration is in an invalid format");
}
if ($f[$c] !== undefined && $f[$c][split[1]]) {
value = $f[$c][split[1]];
}
else if ($c[split[1]]) {
value = $c[split[1]];
}
else if ($f[split[1]]) {
value = $f[split[1]];
}
else {
value = "" + split[1];
}
var key = split[0];
result[key] = value;
return result;
};
Everything appears to work as it did previously, but this modification now handles the use case where the declarations variable could have a dash in it (EX: "value:background-color"). Additionally the declarations variable is passed into the function, to ensure it's defined.

Jasmine expect logic (expect A OR B)

I need to set the test to succeed if one of the two expectations is met:
expect(mySpy.mostRecentCall.args[0]).toEqual(jasmine.any(Number));
expect(mySpy.mostRecentCall.args[0]).toEqual(false);
I expected it to look like this:
expect(mySpy.mostRecentCall.args[0]).toEqual(jasmine.any(Number)).or.toEqual(false);
Is there anything I missed in the docs or do I have to write my own matcher?
Add multiple comparable strings into an array and then compare. Reverse the order of comparison.
expect(["New", "In Progress"]).toContain(Status);
This is an old question, but in case anyone is still looking I have another answer.
How about building the logical OR expression and just expecting that? Like this:
var argIsANumber = !isNaN(mySpy.mostRecentCall.args[0]);
var argIsBooleanFalse = (mySpy.mostRecentCall.args[0] === false);
expect( argIsANumber || argIsBooleanFalse ).toBe(true);
This way, you can explicitly test/expect the OR condition, and you just need to use Jasmine to test for a Boolean match/mismatch. Will work in Jasmine 1 or Jasmine 2 :)
Note: This solution contains syntax for versions prior to Jasmine v2.0.
For more information on custom matchers now, see: https://jasmine.github.io/2.0/custom_matcher.html
Matchers.js works with a single 'result modifier' only - not:
core/Spec.js:
jasmine.Spec.prototype.expect = function(actual) {
var positive = new (this.getMatchersClass_())(this.env, actual, this);
positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
return positive;
core/Matchers.js:
jasmine.Matchers = function(env, actual, spec, opt_isNot) {
...
this.isNot = opt_isNot || false;
}
...
jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
return function() {
...
if (this.isNot) {
result = !result;
}
}
}
So it looks like you indeed need to write your own matcher (from within a before or it bloc for correct this). For example:
this.addMatchers({
toBeAnyOf: function(expecteds) {
var result = false;
for (var i = 0, l = expecteds.length; i < l; i++) {
if (this.actual === expecteds[i]) {
result = true;
break;
}
}
return result;
}
});
You can take the comparison out of the expect statement to gain full use of comparison operators.
let expectResult = (typeof(await varA) == "number" || typeof(await varA) == "object" );
expect (expectResult).toBe(true);

Javascript consuming variables when variable a = variable b

I have a script setup like this (http://jsfiddle.net/YD66s/):
var countFull = new Array(0,1,2,3,4,5,6);
var countActive = new Array(0,1,2,3,4,5,6);
function pickRandom(a) {
if(arguments[1].length == 0) {
arguments[1] = arguments[0];
}
var m = Math.floor(Math.random()*arguments[1].length);
chosen = arguments[1].splice(m,1);
return chosen;
}
setInterval(function() {
pickRandom(countFull,countActive);
}, 1000);
When I run this I want the variable to be set for that function only. Instead it is affecting countFull towards the end because I make arguments[1] = arguments[0]. How in javascript can I just reference a variable but not consume it and ultimately arguments[1] becomes arguments[0].
Hope this makes sense. This is driving me nuts how different javascript variables are compared to other languages like PHP.
Javascript arrays are just pointers so when you do arguments[1] = arguments[0] you actually just set the pointer but the underlying arrays are the same. As a result, every time you modify arguments[1] you also modify arguments[0]. To do what you want, you need to copy the array. You could do it this way:
if (arguments[1].length == 0) {
for(var i = 0; i < arguments[0].length; i++) {
arguments[1][i] = arguments[0][i];
}
}
To copy an array, instead of referencing it, use copy = original.slice(0).

Multiple OR operators with elem.value.match

I've been writing a javascript function which returns true if the value matches one of about 4 values (just 3 in the example below). The problem is, when I have just two values the function works correctly, but adding a third breaks the code.
I'm pretty new to javascript and I'm guessing there's a much better way of doing this? I've tried searching but found nothing as of yet.
Any help is much appreciated.
function isValid(elem, helperMsg){
var sn6 = /[sS][nN]6/;
var sn5 = /[sS][nN]5/;
var sn38 = /[sS][nN]38/;
if(elem.value.match(sn6 || sn5 || sn38)){
//do stuff
return true;
}else{
return false;
}
}
Edit:
Here's my second attempt with an array:
function isLocal(elem, helperMsg){
var validPostcodes=new Array();
validPostcodes[0]= /[wW][rR]12/;
validPostcodes[1]= /[cC][vV]35/;
validPostcodes[2]= /[sS][nN]99/;
validPostcodes[3]= /[sS][nN]6/;
validPostcodes[4]= /[sS][nN]5/;
validPostcodes[5]= /[sS][nN]38/;
validPostcodes[6]= /[oO][xX]29/;
validPostcodes[7]= /[oO][xX]28/;
var i = 0;
for (i = 0; i < validPostcodes.length; ++i) {
if(elem.value.match(validPostcodes[i])){
// do stuff
return true;
}else{
alert(helperMsg);
elem.focus();
return false;
}
}
}
a || b || c
is an expression that evaluates to a boolean. That means that you're running either match(true) or match(false). You must write it as:
match(a) || match(b) || match(c)
Another option would be to store them in an array and loop over it. That would mean if the number of patterns grew you wouldn't have to change code other than the list of patterns. Another approach, though limited to this situation, might be to change the pattern to one that is equivalent to or-ing the three options together (untested, and I'm a bit rusty on regex):
elem.value.match(/[sSnN][6|5|38]/)
Array based example:
var patterns = [/../, /.../];
for (var i = 0; i < patterns.length; ++i) {
if (elem.value.match(patterns[i])) { return true; }
}
In real code, I would probably format it like this:
function isValid(elem, helperMsg){
var patterns = [/../, /.../],
i = 0;
for (i = 0; i < patterns.length; ++i) {
if (elem.value.match(patterns[i])) {
return true;
}
}
}
That's just a habit though since JavaScript hoists variables to the top of their scope. It's by no means required to declare the variables like that.

Categories