How to check in the most simple way if two objects are equal? I noticed that there's no method ExactEqual()-like, so I wonder if I should do it manually or not.
Thanks for any help!
There is no such method. Take a look at this jqfaq.com link where it was discussed and given the best way to check. This will suitable for both typescript and javascript. Hope, that will help you!.
To quote the most important part of that answer:
//“===” means that they are identical.
//“==” means that they are equal in value.
//( == )
//Each JavaScript value is of a specific “type” (Numbers, strings, Booleans, functions, and objects). So if you try to compare a string with a number, the browser will try to convert the string into a number before doing the comparison.
//So the following will return true.
55 == “55″ //true
0 == false //true
1 == true //true
//(===)
//The === operator will not do the conversion, if two values are not the same type it will return false. In other words, this returns true only if the operands are strictly equal in value or if they are identical objects.
55 === “55″ //false
0 === false //false
1 === true //false
var a = [1, 2, 3];
var b = [1, 2, 3];
var c = a;
var is_ab_eql = (a === b); // false (Here a and b are the same type,and also have the same value)
var is_ac_eql = (a === c); // true.
//Value types (numbers):
//a === b returns true if a and b have the same value and are of the same type.
//Reference types:
//a === b returns true if a and b reference the exact same object.
//Strings:
//a === b returns true if a and b are both strings and contain the exact same characters.
var a = “ab” + “c”;
var b = “abc”;
a === b //true
a == b //true
//in thiss case the above condition will fail
var a = new String(“abc”);
var b = “abc”;
a === b //false
a == b// true
//… since a and b are not a same type.
typeof “abc”; // ‘string’
typeof new String(“abc”)); // ‘object
Related
I would like to understand the weird behaviour of JavaScript identity and equality operator as given below.
var a = {};
var b = {};
a === b; //false
a == b; //false
var c = '';
var d = '';
c === d; //true
c == d; //true
All four variables a ,b ,c and d are objects. But when comparing them, first case yields false whereas second one true.
I studied comparison from the following source: https://msdn.microsoft.com/en-us/library/d53a7bd4(v=vs.94).aspx
According to the above article, except number and boolean everything is compared by reference instead of value. So how the first case returns false and second one true.
c and d in your example are strings, which are primitive types in JavaScript and compared by value.
For this reason, c == d returns true.
The article is talking about string objects created usng the new String('foo') constructor, which actually creates objects. In this case, the references are compared, returning false.
console.log(new String('foo') == new String('foo')) // false
console.log('foo' == 'foo') // true
A (primitive) string is a value type (like Number). As such === compares its value (equality).
Objects are reference types, and for them === compares identity.
Strings are a bit crazy, as there are both primitive strings and String objects (created with new String("foo")).
== works the same way as === except that it does type conversions to "make things equal if possible". For reference types this is the same as ===, except that it also equates primitive strings and String objects.
"abc" == new String("abc");
"abc" !== new String("abc");
I was looking through jQuery code $.extend()
I found this:
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
I was wondering what would happen if we move options = arguments[i] out of parenthesis?
a = b assigns b to a and returns b. Thus, (a = b) != c will assign b to a and then check b != c. The parentheses are because otherwise, != will be evaluated before = due to the operator precedence (comparison is evaluated before assignment - source)
a = b!= c
will work like,
a = (b != c)
since = operator has least precedence in all the operators.
e.g.
b = 5
c = 10
a = b != c
>>> false
>>> a will have false value here
Here is the precedence chart and demo
In case of (options = arguments[ i ]) != null, options will be assigned a value of argument[ i ] and then compared with null.
Assignment has less precedence than equality which means
arguments[i] != null
would be processed before
options = arguments[i]
Given a different result than what is desired.
To understand it a little better check here and read about operator precedence
If I have code that looks like this with a test for ===
var a = (b === null) ? "" : 888;
then is this the same as just placing the variable name inside () ?
var a = (b) ? "" : 888;
b === null will check if b have same type, in your case null, just checking for (b) will check for undefined, Boolean, Int, null and String values.
Basically == Just compare two variables value, and if requires it will convert datatypes implicitely.
while === will compare two variables value with it's datatype.
Consider this simple example,
0==false // true
0===false // false, because they are of a different type
1=="1" // true, auto type coercion
1==="1" // false, because they are of a different type
this is how == and === work.
in javascript we can easily cast all kinds of values to a boolean just with using them as a boolean, and there are some values which presents the false value like:
null
""
undefined
0
false
the other point is, == compares the values of object and works with valueOf() objects,
but === compare the object instances.
For example:
var MyClass = function(){};
MyClass.prototype.valueOf = function(){return 12;};
var obj = new MyClass();
console.log(obj==12);//result is true
BUT
console.log(obj===12);//result is false
the point is when you compare an object with null like:
obj === null
it would compare them as 2 different objects, but if you do it like:
obj == null; //better way to check the value
it would compare the values:
var myobj = undefined;
console.log(myobj==null);//result is true
But instead of all these lines of codes if you really want a Boolean value you can do it like:
obj = !!obj;//this is something like a toBoolean method.
No, it is not the same.
=== means strict equality, the left hand side and right hand side need to be equal in both type and value.
using the if (a) construct, a is checked to be truthy or falsy. Falsy values are for example "" (string), 0 (number), undefined and null.
if (a) is almost the same as if (a !== null && a !== undefined && a !== "" && a !== 0)
I need a function:
function isSame(a, b){
}
In which, if a and b are the same, it returns true.
, I tried return a === b, but I found that [] === [] will return false.
Some results that I expect this function can gave:
isSame(3.14, 3.14); // true
isSame("hello", "hello"); // true
isSame([], []); // true
isSame([1, 2], [1, 2]); // true
isSame({ a : 1, b : 2}, {a : 1, b : 2}); //true
isSame([1, {a:1}], [1, {a:1}]); //true
You could embed Underscore.js and use _.isEqual(obj1, obj2).
The function works for arbitrary objects and uses whatever is the most efficient way to test the given objects for equality.
the best way to do that is to use a JSON serializer. serialize both to string and compare the string.
There are some examples, adapted from scheme, on Crockford's site. Specifically, check out:
function isEqual(s1, s2) {
return isAtom(s1) && isAtom(s2) ? isEqan(s1, s2) :
isAtom(s1) || isAtom(s2) ? false :
isEqlist(s1, s2);
}
It can all be found here:
http://javascript.crockford.com/little.js
Here is a working example:
http://jsfiddle.net/FhGpd/
Update:
Just wrote some test cases based on the OP. Turns out I needed to modify the sub1 function to check <= 0 not === 0 otherwise isEqual(3.14, 3.14) blew the stack. Also, isEqual does not work for object comparison, so you are on your own there. However, if you follow the examples on Crockford's site you will see how easy and fun it is to write recursive methods that could be used to check for object equality.
Here is something that can work:
function isSame(obj1, obj2, prefer){
// Optional parameter prefer allows to only check for object keys and not both keys and values of an object
var obj_prefer = prefer || "both";
function checkArray(arr1, arr2){
for(var i = 0, j = obj1.length; i<j; i++){
if(obj1[i] !== obj2[i]){return false;}
}
return true;
}
function checkValues(obj_1, obj_2){
for(var prop in obj_1){
if(typeof obj_1[prop] === "function"){ // converting functions to string so that they can be matched
obj_1[prop] = String(obj_1[prop]);
obj_2[prop] = String(obj_2[prop]);
}
if(obj_1[prop] !== obj_2[prop]){ return false;}
}
return true;
}
// The built in === will check everything except when typeof object is "object"
if ( typeof obj1 === "object"){
// typeof Array is object so this is an alternative
if((typeof obj1.push === "function") && (!obj1.hasOwnProperty('push'))){
return checkArray(obj1, obj2);
}
else{
if( obj_prefer !== "keys"){ // do not check for values if obj_prefer is "keys"
return checkValues(obj1, obj2);
}
var keys_1 = Object.keys(obj1);
var keys_2 = Object.keys(obj2);
if(!checkArray(keys_1, keys_2)){return false;}
return true;
}
}
// I thought undefined === undefined will give false but it isn't so you can remove it
if( typeof obj1 === "undefined" && typeof obj2 === "undefined" ){return true}
if(typeof obj1 === "function"){
return String(obj1) === String(obj2);
}
return obj1 === obj2;
}
console.log(isSame(2, 2)); //true
console.log(isSame([1], [1])); // true
Since it converts Functions into Strings to compare them, check out for spaces as that can break things:
var func1 = function(){},
func2 = function(){ }; // function with extra space
isSame(func1, func2); // false
You can check out http://jsfiddle.net/webholik/dwaLN/4/ to try it yourself.
If anyone reading this answer is using Angular.js, you can use angular.equals(obj1,obj2);
According to the docs:
Determines if two objects or two values are equivalent. Supports value
types, regular expressions, arrays and objects.
Two objects or values are considered equivalent if at least one of the
following is true:
Both objects or values pass === comparison.
Both objects or values are of the same type and all of their properties are equal by comparing them with angular.equals.
Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal).
Both values represent the same regular expression (In JavaScript, /abc/ == /abc/ => false. But we consider two regular expressions as
equal when their textual representation matches).
During a property comparison, properties of function type and properties with names that begin with $ are ignored.
Scope and DOMWindow objects are being compared only by identify (===).
if (typeof a !== "object" && typeof b !== "object") {
return a == b;
}
... // check pairwise equality of object a & b using `for in`
Is it the same as
if (typeof a !== "object") {
return a == b;
}
Is there any b with typeof b === "object" which would change the semantics?
Are there any horrible edge cases I should be aware of? Comparisons between an object and a native type which have a non-intuitive boolean equality or disequality? Including any bugs in browser (I mean you IE6!)
The second check is not quite the same as the first, no, simply because JavaScript is weakly typed so at the very least consider the ".toString() effect", as well as others. For example these would fail the first check, but pass in the second:
var a = "[object Object]";
var b = {};
Or, a bit simpler (showing a case you may want to consider...but this passes both checks):
var a = 0;
var b = "0";
One fix would be to do a value and type check with === which is a strict comparison operator, you get type checking as well...but I'm not entirely sure that's what you're after, since the current check is explicitly "not an object".
it's not the same.
here's an example to understand why :
var a = "a string";
var b = new String("a string");
console.log(typeof a);//"string" !== "object"
console.log(typeof b);//"object" === "object"
console.log('"' + a + '" ' + (a==b?"==":"!=") + ' "' + b + '"');
the same thing can happen for numbers :
var a = 1;
var b = new Number(1);
your two if statements are not the same for the obvious reason.
you could "improve" your type check by using the instanceof operator to suite tour needs :
if ((typeof a !== "object" || a instanceof Number || a instanceof String) &&
(typeof b !== "object" || b instanceof Number || b instanceof String))
return a == b;
Take a look at isEqual from Underscore.js. It "Performs an optimized deep comparison between the two objects, to determine if they should be considered equal." It works for all types of variables. This is how it's implemented:
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
// Check object identity.
if (a === b) return true;
// Different types?
var atype = typeof(a), btype = typeof(b);
if (atype != btype) return false;
// Basic equality test (watch out for coercions).
if (a == b) return true;
// One is falsy and the other truthy.
if ((!a && b) || (a && !b)) return false;
// Unwrap any wrapped objects.
if (a._chain) a = a._wrapped;
if (b._chain) b = b._wrapped;
// One of them implements an isEqual()?
if (a.isEqual) return a.isEqual(b);
// Check dates' integer values.
if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
// Both are NaN?
if (_.isNaN(a) && _.isNaN(b)) return false;
// Compare regular expressions.
if (_.isRegExp(a) && _.isRegExp(b))
return a.source === b.source &&
a.global === b.global &&
a.ignoreCase === b.ignoreCase &&
a.multiline === b.multiline;
// If a is not an object by this point, we can't handle it.
if (atype !== 'object') return false;
// Check for different array lengths before comparing contents.
if (a.length && (a.length !== b.length)) return false;
// Nothing else worked, deep compare the contents.
var aKeys = _.keys(a), bKeys = _.keys(b);
// Different object sizes?
if (aKeys.length != bKeys.length) return false;
// Recursive comparison of contents.
for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
return true;
};
See the Underscore.js source code to see the rest of the functions used by this one.
It's easy to miss some edge cases so I would recommend using a well tested code like this one instead of reinventing the wheel.