Testing for equality of regular expressions - javascript

I was surprised to see that
/a/ === /a/
evaluates to false in JavaScript. Reading through the specs:
Two regular expression literals in a program evaluate to regular
expression objects that never compare as === to each other even if the
two literals' contents are identical.
Since === cannot be used to test for equality, how can equality of regular expressions be tested in JavaScript?

Here's a case that even covers ordering of flags.
function regexEqual(x, y) {
return (x instanceof RegExp) && (y instanceof RegExp) &&
(x.source === y.source) && (x.global === y.global) &&
(x.ignoreCase === y.ignoreCase) && (x.multiline === y.multiline);
}
Tests:
regexEqual(/a/, /a/) // true
regexEqual(/a/gi, /a/ig) // also true.
regeXEqual(/a/, /b/) // false

Here's a function that fully tests all the relevant regex properties and makes sure it's the right type of object:
function regexSame(r1, r2) {
if (r1 instanceof RegExp && r2 instanceof RegExp) {
var props = ["global", "multiline", "ignoreCase", "source", "dotAll", "sticky", "unicode"];
for (var i = 0; i < props.length; i++) {
var prop = props[i];
if (r1[prop] !== r2[prop]) {
return false;
}
}
return true;
}
return false;
}
And, since flags sometimes get added to the regex object with new features (as has happened since this original answer in 2012 - though the above code has been updated as of 2019), here's a version that is a bit more future proof on future flags being added since it compares whatever flags are there rather than looking for a specific set of flags. It sorts the flags before comparing to allow for minor differences in how the regex was specified that wouldn't not actually change functionality.
function regexSame(r1, r2) {
return r1 instanceof RegExp &&
r2 instanceof RegExp &&
r1.source === r2.source &&
r1.flags.split("").sort().join("") === r2.flags.split("").sort().join("");
}

Compare them using toString(), and check their type too:
var a = /a/,
b = /a/;
a.toString() === b.toString() && typeof(a) === typeof(b) //true
var c = /a/,
d = /b/;
c.toString() === d.toString() && typeof(c) === typeof(d) //false

You can check the types with typeof, then toString() both regexes and compare those. It won't cover cases with equivalent flags, such as /a/gi and /a/ig, though.
function regexEquals(a, b)
{
if (typeof a !== 'object' || typeof b !== 'object') return false;
return a.toString() === b.toString();
}
Unfortunately there's no more-specific type from typeof, so if you really want to make sure they're regexes (or regex-like) you could do something along these lines:
RegExp.prototype.regexEquals = function (other)
{
return (typeof other.regexEquals === 'function')
&& (this.toString() === other.toString());
}
Then:
/a/.regexEquals(/a/); // true
/a/.regexEquals(/b/); // false

Answers above didn't consider case-sensitivity. So built upon jfriend00's answer, the function should be
function regexEqual(a, b) {
if (!(a instanceof RegExp) || !(b instanceof RegExp)) {
return false;
}
let sourceA = a.source;
let sourceB = b.source;
const flagsA = a.flags.split('').sort().join(',');
const flagsB = b.flags.split('').sort().join(',');
if (flagsA.includes('i') && flagsB.includes('i')) {
sourceA = sourceA.toLowerCase();
sourceB = sourceB.toLowerCase();
}
return sourceA === sourceB && flagsA === flagsB;
}

Related

You Don't Know JS: Up & Going - example problem

In page 38 I found - arrays are by default coerced to strings by simply
joining all the values with commas (,) in between. You might think
that two arrays with the same contents would be == equal, but
they’re not:
var a = [1,2,3];
var b = [1,2,3];
var c = "1,2,3";
a == c; // true
b == c; // true
a == b; // false
But when I run my code like the following:
var a = [1,2,3];
var b = [1,2,3];
var c = "1,2,3";
console.log(typeof a == c); // false
console.log(typeof b == c); // false
console.log(typeof a == b); // false
I got different answers! Why typeof a == c and typeof b == c is false here?
its doing
typeof a == c
=> (typeof a) == c
=> "object" == c // which is false
basically typeof operator works before == sign
My cliche advice, always use paranthesis to make your intent clear
typeof (a==b) vs (typeof a) == b
The typeof operator returns the type of the content of a variable as a string. So if your variable a contains an array, typeof a will return the string "object" (as arrays are a special kind of objects).
So typeof a == c and typeof b == c compare the string "object" with the string "1,2,3". As they are obviously not the same, this returns false.
Note that the typeof operator has a higher precedence than the == operator.

String validation function in JavaScript

I'm trying to create a nice util function to validate strings. Conditions are:
Cannot be typeof "undefined"
Cannot be null
Must be a string i.e. not a number, object, array, etc.
Must have at least one character in it. So a string with value of '' would be invalid.
I found some pointers on this that suggest using RegEx is the way to do it but the following is not working when I give it a numeric value.
Here's what I have so far:
const isValidString = (str1) => {
if(typeof str1 === "undefined" || str1 === null) return false;
const validRegEx = /^[^\\\/&]*$/;
if(str1.match(validRegEx)) {
return true;
} else {
return false;
}
}
As I said, if send const str1 = 3; to this function, I get an error that reads:
"TypeError: badString.match is not a function
What am I missing?
if (str1 != null) will coerce the str1 value, and will check both against null and undefined.
EDIT: ignore this part (Also, you don't need to use a regex to check if there's at least one character. You can use the string length. str1.length will evaluate to a falsey value if it's 0, otherwise it will evaluate to a truethy value.) You need a strict boolean value, so it should be str1.length > 0.
function isValidString(str1) {
return str1 != null && typeof str1 === "string" && str1.length > 0;
}
Why don't you add a typeof str1 === 'string' as shown below
if(typeof str1 === 'string' && str1.match(validRegEx)) {
return true;
} else {
return false;
}
That is because the match method is only available on strings, and you are attempting to call it on a number.
I would suggest re-writing the function to ensure that the argument is a string, rather than only checking for null or undefined values:
const isValidString = str1 => {
if (!str1 || typeof str1 !== 'string') return false
const validRegEx = /^[^\\\/&]*$/
if (str1.match(validRegEx)) {
return true
} else {
return false
}
}
Convert number to string perform evaluating it
const isValidString = (str1) => {
str1 = str1.toString();
if(typeof str1 === "undefined" || str1 === null) return false;
const validRegEx = /^[^\\\/&]*$/;
if(str1.match(validRegEx)) {
return true;
} else {
return false;
}
}
console.log(isValidString(3))
Replace the if with following:
if(typeof str1 === "undefined" || str1 === null || typeof str1 !== 'string') return false;
Use RegExp#test instead, because your regular expression will always be available. Also, String#match returns an array (or null) which is not well-suited for boolean expressions and may produce unwanted results.
const isValidString = (str1) => {
if (typeof str1 === "undefined" || str1 === null) return false;
const validRegEx = /^[^\\\/&]*$/;
if (validRegEx.test(str1)) {
return true;
} else {
return false;
}
}
console.log(isValidString(1));
console.log(isValidString('string'));
Note, your regexp is still wrong because it validates the number as true. You need to tweak it more.
Credit to djfdev on this one, all I've really done is simplify his answer to be more succinct.
const isValidString = str1 => {
return typeof str1 === 'string' && /^[^\\\/&]*$/.test(str1);
}

Combined Comparison / "Spaceship" Operator (<=>) in Javascript?

Ruby has something called a Combined Comparison or "Spaceship" Operator, it looks like this: <=>
It does the following:
a <=> b :=
if a < b then return -1
if a = b then return 0
if a > b then return 1
Credit
Is there a similar Operator in Javascript? If not, how can I end up with the same result?
#madox2 suggested using Math.sign(a - b), which works for number, but not arrays (to compare arrays you need to use array.length).
It also does not work in Internet Explorer, Safari or all Mobile Browsers (see MDN)
#duques_l found a function here. It works very well, you can test it on JSFiddle
The only problem is if the strings are not comparable the function returns -1 instead of nil
Update: #duques_l changed the function a bit and now it works fine (I think so anyway, here is the JSFiddle):
function spaceship(val1, val2) {
if ((val1 === null || val2 === null) || (typeof val1 != typeof val2)) {
return null;
}
if (typeof val1 === 'string') {
return (val1).localeCompare(val2);
}
else {
if (val1 > val2) { return 1 }
else if (val1 < val2) { return -1 }
return 0;
}
}
As far as I know there is no such operator in JavaScript but you can use Math.sign() function:
Math.sign(a - b);
NOTE: As was mentioned in comments, Math.sign() is not currently supported by all browsers. Check for compatibility (MDN).
from: http://sabrelabs.com/post/48201437312/javascript-spaceship-operator
improved version:
function spaceship(val1, val2) {
if ((val1 === null || val2 === null) || (typeof val1 != typeof val2)) {
return null;
}
if (typeof val1 === 'string') {
return (val1).localeCompare(val2);
} else {
if (val1 > val2) {
return 1;
} else if (val1 < val2) {
return -1;
}
return 0;
}
}
Here is a one-liner for current js ES2020?:
const spaceship = (a, b) => (a ?? 0).toString().localeCompare((b ?? 0).toString());
Returns string comparison for everything.

Javascript full comparison function

I am developing a complete library (in Javascript) containing main structures and algorithms.
I need to design a compare function in order to compare every kind of data.
I’ll use this function as:
a = some data (string, number, object, array, ...)
b = some data (string, number, object, array, ...)
b.compare(a) :
0 if a is equal to b
1 if b i greater than
-1 if b is less than a
I designed my version and I got inspired by _.eq of underscore.js framework.
What do you think about it?
Is there any better way to implement it?
function compare (a, b, aStack, bStack) {
// with this I can youse compare as b.compare(a)
b = b || this.data;
// Unwrap any wrapped objects.
if (b instanceof ObjectNode) b = b.data;
if (a instanceof ObjectNode) a = a.data;
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) {
if(a !== 0) return 0;
else if(1 / a === 1 / b) return 0;
else return (1 / b > 1 / a)? 1 : -1;
}
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null || a == undefined || b == undefined) {
if(a === b) return 0;
/* Now I am defining:
(NaN > null)
null > undefined
null < everything else
undefined < everything */
if(a == undefined) return 1;
if(b == undefined) return -1;
if(a == null) return 1;
}
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className !== toString.call(b)) {
// In this case I compare strings;
if(className < toString.call(b)) 1;
if(className > toString.call(b)) -1;
}
switch (className) {
// Strings, numbers, regular expressions, dates, and booleans are compared by value.
case '[object RegExp]':
// RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
if('' + a === '' + b) return 0;
if('' + a < '' + b) return 1;
if('' + a > '' + b) return -1;
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive.
// Object(NaN) is equivalent to NaN
// Nan is less than anyother but bigger than undefined and than null
if (+a !== +a) return (+b !== +b)? 0 : 1;
// An `egal` comparison is performed for other numeric values.
if (+a === 0) return (1 / +b === 1 / a)? 0 : ((1 / +b > 1 / a)? 1 : -1);
return (+a === +b)? 0 : (+b > +a)? 1 : -1;
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return (+a === +b) ? 0 : (+b > +a)? 1 : -1;
}
var areArrays = className === '[object Array]';
if (!areArrays) {
if (typeof a != 'object') return 1;
if (typeof b != 'object') return -1;
// Objects with different constructors are not equivalent, but `Object`s or `Array`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !((typeof aCtor == 'function') && aCtor instanceof aCtor &&
(typeof bCtor == 'function') && bCtor instanceof bCtor)
&& ('constructor' in a && 'constructor' in b)) {
return b>a? 1 : -1;
}
}
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
// Initializing stack of traversed objects.
// It's done here since we only need them for objects and arrays comparison.
aStack = aStack || [];
bStack = bStack || [];
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] === a) return bStack[length] === b;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
// Recursively compare objects and arrays.
if (areArrays) {
// Compare array lengths to determine if a deep comparison is necessary.
length = a.length;
if (length !== b.length) return b.length>a.length? 1 : -1;
// Deep compare the contents, ignoring non-numeric properties.
var res;
while (length--) {
if ( (res = this.compare(a[length], b[length], aStack, bStack)) != 0) return res;
}
} else {
// Deep compare objects.
var keys = Object.keys(a), key;
length = keys.length;
// Ensure that both objects contain the same number of properties before comparing deep equality.
if (Object.keys(b).length !== length) return (Object.keys(b).length > length)? 1 : -1;
while (length--) {
// Deep compare each member
key = keys[length];
if (!(hasOwnProperty.call(b, key) && (res = this.compare(a[key], b[key], aStack, bStack)) === 0)) return res;
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
return 0;
}

Best and/or shortest way to do strict (non-type converting) <, >, <=, >= comparison in Javascript

In Javascript, the == comparison has a strict (non-type converting) version: ===. Likewise, != has the strict form !==. These protect you from the following craziness:
var s1 = "1",
i1 = 1,
i2 = 2;
(s1 == i1) // true, type conversion
(s1 != i1) // false, type conversion
(s1 === i1) // false, no type conversion
(s1 !== i1) // true, no type conversion
However, the other comparison operators have no equivalent strict modes:
(s1 < i2) // true, type conversion
(s1 <= i2) // true, type conversion
([] < i2) // true, wait ... wat!?
The obvious solution seems pretty verbose:
((typeof s1 === typeof i2) && (s1 < i2)) // false
Is there a more idiomatic (or just less verbose) way to do this in Javascript?
Reference: MDN Comparison Operators
There are no built-in operators for what you want, but you can always create your own functions. For example, for <:
function lt(o1, o2) {
return ((typeof o1 === typeof o2) && (o1 < o2));
}
lt("10", 11); // false
Another option, if you're only dealing with strings and numbers, is extending String.prototype and Number.prototype:
function lt(o) {
return ((typeof this.valueOf() === typeof o) && (this < o));
}
String.prototype.lt = lt;
Number.prototype.lt = lt;
"10".lt(11); // false
(11).lt("12"); // false
How about creating a Object and using it
var strictComparison = {
"<" : function(a,b) { return ((typeof a === typeof b) && (a < b)) },
"<=" : function(a,b) { return ((typeof a === typeof b) && (a <= b)) },
">" : function(a,b) { return ((typeof a === typeof b) && (a > b)) },
">=" : function(a,b) { return ((typeof a === typeof b) && (a >= b)) }
};
console.log(strictComparison["<"](5,"6")) ;
console.log(strictComparison[">"](5,6)) ;

Categories

Resources