Concept: Comparing objects and arrays like atrings and numbers in JavaScript [duplicate] - javascript

This question already has answers here:
Object comparison in JavaScript [duplicate]
(10 answers)
Closed 5 years ago.
Is there a way to compare two un-referenced objects by their literal value like a string or a number?
_is = function (a, b) { /* Code */ }
This could apply to any object type, even custom objects.
_is(new X("a"), new X("a")); // Returns True;
_is(new X("a"), new Y("a")); // Returns False
You could convert it into a string, but that would be sloppy.
JSON.stringify({ x: "a" }) == JSON.stringify({ x: "a" }); // Returns True
Maybe there's a way to programatically read each key, subkey, and value of the object, and compare that way.
Any ideas?

Javascript hashes objects in memory with unique memory pointers. You're essentially creating two separate objects, and trying to compare them. Despite the fact that they look similar, they in fact are different.
Try storing the object as a variable first, and then testing against that variable.
pass = arg => { return arg }; pass({ a: "x" }); // Returns The Input
pass(({ a: "x" })) == ({ a: "x" }); // Returns False
var obj = {a: "x"};
pass(obj) === (obj); // Returns true
console.log(pass(obj) === (obj));
UPDATE:
If you want to compare two separate objects, the best and most efficient way I've found is to create a hash of both objects, and then compare the hashes (basically comparing two checksums). If the hashes match, the objects are the same. If the hashes differ, they are different objects. You can assume that the properties and values are the same in each object, since using a hashing function, even hashing functions used in some forms of crypto like md5 and sha can be utilized to generate a unique hash of the object.
Here's a 3rd Party Library I've used before to generate object hashes.

Related

Boolean comparison explanation [duplicate]

This question already has answers here:
How to compare arrays in JavaScript?
(61 answers)
Why are two identical objects not equal to each other?
(9 answers)
Closed 2 years ago.
I started with:
"1:2".split(':') == ["1","2"];
// false
Then tried:
[1,2] == [1,2];
// false
and ultimately:
[] == [];
// false
I've since found that:
"1:2".split(':').toString() == [1,2].toString();
// true
So I've solved my initial issue (kind of) but why can't arrays match each other?
Javascript arrays are objects and you can't simply use the equality operator == to understand if the content of those objects is the same. The equality operator will only test if two object are actually exactly the same instance (e.g. myObjVariable==myObjVariable, works for null and undefined too).
If you need to check if two array are equals i'd recommend to just traverse both arrays and verify that all the elements have the same value (and that the two array have the same length).
Regarding custom objects equality i'd build instead a specific equals function and i'd add it to the prototype of your class.
Considering that in the end you converted both arrays to a String and tested equality of the resulting strings, you could one day consider using a similar but more generic technique you'll find described in more than a few places:
JSON.stringify(OBJ1) === JSON.stringify(OBJ2)
Well, don't.
While this could work if the order of the properties will always the same for those object instances, this leaves the door open for extremely nasty bugs that could be hard to track down. Always favor a more explicit approach and just write a clean and readable function that will test for equality checking all the required fields.
The == operator for Objects in Javascript only checks to see if the objects are the same actual object reference, not if they are two separate object that contain the same contents. There is no built in operator for checking if they contain the same contents. You would have to write a function to do that sort of comparison yourself.
Your string conversion is one way of comparing two arrays as long as the array elements only contain primitive values (not other objects). If the array elements could contain other elements, then you would have to make sure those objects were themselves converted into representative strings too.
And, converting to a string would not discern between an array element that contains "4" versus one that contains 4 since both convert to "4" in the string representation.
FYI, you can read in this answer about records and tuples, two new data types coming to future Javascript that will allow comparing an immutable version of arrays and objects by value instead of by only comparing to see if they are the same object.
Equality for objects will tell you if the two objects are the same one.
var a = [];
var b = a;
a === b; // True, a and b refer to the same object
[] === []; // False, two separate objects
You will have to loop through the arrays to see if they have the same elements.
See: How to check if two arrays are equal with JavaScript?
In javascript each [] is an instance of window.Array class. So you are basically trying to compare two different objects. Since array's can have any no. and any type of elements including Objects and Custom Objects and those nested arrays can again have numerous properties and arrays and so on.
It becomes ambiguous when it comes to comparison, you will never be sure what do you want to do with those objects and nested properties. So what you are trying to achieve by comparing can be done in so many other ways. You just have to figure out the right way for your case.
One way is to make your own Array checking function:
How to compare arrays in JavaScript?!
Another way is to convert the Array to a String with .join(), and compare the strings. Then convert them back into Arrays with .split().
If I relate this problem with that in Python:
Input:
a="1 2 3 4"
case I:
a=input.split(' ')
output: ['1', '2', '3', '4']
case II:
a=map(int,input.split(' '))
output: [1, 2, 3, 4]
So, the fault is that of type, as it could come-out aa 'true' for:
"1:2".split(':').toString() == [1,2].toString(); //true

Why JavaScript returns false when I compare with "===" two identical arrays [duplicate]

This question already has answers here:
How to compare arrays in JavaScript?
(61 answers)
Why are two identical objects not equal to each other?
(9 answers)
Closed 2 years ago.
I started with:
"1:2".split(':') == ["1","2"];
// false
Then tried:
[1,2] == [1,2];
// false
and ultimately:
[] == [];
// false
I've since found that:
"1:2".split(':').toString() == [1,2].toString();
// true
So I've solved my initial issue (kind of) but why can't arrays match each other?
Javascript arrays are objects and you can't simply use the equality operator == to understand if the content of those objects is the same. The equality operator will only test if two object are actually exactly the same instance (e.g. myObjVariable==myObjVariable, works for null and undefined too).
If you need to check if two array are equals i'd recommend to just traverse both arrays and verify that all the elements have the same value (and that the two array have the same length).
Regarding custom objects equality i'd build instead a specific equals function and i'd add it to the prototype of your class.
Considering that in the end you converted both arrays to a String and tested equality of the resulting strings, you could one day consider using a similar but more generic technique you'll find described in more than a few places:
JSON.stringify(OBJ1) === JSON.stringify(OBJ2)
Well, don't.
While this could work if the order of the properties will always the same for those object instances, this leaves the door open for extremely nasty bugs that could be hard to track down. Always favor a more explicit approach and just write a clean and readable function that will test for equality checking all the required fields.
The == operator for Objects in Javascript only checks to see if the objects are the same actual object reference, not if they are two separate object that contain the same contents. There is no built in operator for checking if they contain the same contents. You would have to write a function to do that sort of comparison yourself.
Your string conversion is one way of comparing two arrays as long as the array elements only contain primitive values (not other objects). If the array elements could contain other elements, then you would have to make sure those objects were themselves converted into representative strings too.
And, converting to a string would not discern between an array element that contains "4" versus one that contains 4 since both convert to "4" in the string representation.
FYI, you can read in this answer about records and tuples, two new data types coming to future Javascript that will allow comparing an immutable version of arrays and objects by value instead of by only comparing to see if they are the same object.
Equality for objects will tell you if the two objects are the same one.
var a = [];
var b = a;
a === b; // True, a and b refer to the same object
[] === []; // False, two separate objects
You will have to loop through the arrays to see if they have the same elements.
See: How to check if two arrays are equal with JavaScript?
In javascript each [] is an instance of window.Array class. So you are basically trying to compare two different objects. Since array's can have any no. and any type of elements including Objects and Custom Objects and those nested arrays can again have numerous properties and arrays and so on.
It becomes ambiguous when it comes to comparison, you will never be sure what do you want to do with those objects and nested properties. So what you are trying to achieve by comparing can be done in so many other ways. You just have to figure out the right way for your case.
One way is to make your own Array checking function:
How to compare arrays in JavaScript?!
Another way is to convert the Array to a String with .join(), and compare the strings. Then convert them back into Arrays with .split().
If I relate this problem with that in Python:
Input:
a="1 2 3 4"
case I:
a=input.split(' ')
output: ['1', '2', '3', '4']
case II:
a=map(int,input.split(' '))
output: [1, 2, 3, 4]
So, the fault is that of type, as it could come-out aa 'true' for:
"1:2".split(':').toString() == [1,2].toString(); //true

How to make sure `has` method of `Map` or `Set` javascript collections compares objects by properties values? [duplicate]

This question already has answers here:
Define a custom hash() method for use with ES6 maps
(2 answers)
Closed 4 years ago.
I couldn't find more information on this but as far as I understand has method from Map and Set collections determines whether a given map or set has an object by checking for reference equality. But I'd like to check whether the objects are equal by comparing their properties.
For example:
let map = new Map();
let obj = {name: "Jack"};
map.set(obj, "myObj");
let result = map.has({name: "Jack"}); //false
result is false but the properties of the objects are identical, so in such case I'd like has to return true.
In Java I'd have to override equals and hashCode methods inside my class to achieve such functionality. Is there something similar I can do in Javascript without overriding has itself?
but the properties of the objects are identical
Really? As this answer outlines, it is usually very hard to define the identity of an object, as it is built of much more than sinple key-value pairs. Therefore it is impossible to derive a unique hash for it to be used in a hashtable, thats why javascript uses the object reference in the Hashtables. It also makes sense as you could get collisions otherwise. Additionally, if you change an object that is included in a hashtable so that it equals to another one, what should happen then?
Now in a lot of cases, you probably know how the object is built and how you can get its identity. Imagine a player that has a unique username, e.g.:
function Player(username, password) {
this.username = username;
this.password = password
}
Now when we want to build a Set to check if an username is already existing, it makes sense to just use the Players username instead of the player itself:
const a = new Player("a", "super secret");
const b = new Player("b", "****");
new Set([a.username, b.username]);
Or you could define a way to build up a unique key from the players properties:
Player.prototype.identity = function() {
return this.username + "°" + this.password;
};
So one can do:
const a = new Player("this is", "equal");
const b = new Player("this is", "equal");
console.log(
a === b, // false
a.identity() === b.identity() // true
);
const players = new Set([a.identity()]);
players.has(b.identity()); // true

How to stringify objects containing ES5 Sets and Maps? [duplicate]

This question already has answers here:
How do I persist a ES6 Map in localstorage (or elsewhere)?
(10 answers)
Convert ES6 Class with Symbols to JSON
(6 answers)
Is there a way of specifying what data of an object is passed to the serializer when it's being serialized?
(2 answers)
Closed 5 years ago.
I am developing an Angular app. I use Typescript and have a certain class which contains the built-in Set and a Map types. My problem is that I want to store instances of my class in the browser's localStorage as JSON, but when I try to stringify them I get empty strings for these types.
Example from chrom's console:
> inst = new Object({
'elem': new Set(),
'str': 'somestring'
})
: Object {elem: Set(0), str: "somestring"}elem: Set(0)str: "somestring"}
> inst.elem.add('123');
: Set(1) {"123"}
> inst.elem.add('123');
: Set(1) {"123"}size: (...)__proto__: Set[[Entries]]: Array(1)0: "123"length: 1
> JSON.stringify(inst)
: "{"elem":{},"str":"somestring"}"
The only thing I could think of is recursively converting Sets and Maps to Arrays and Objects respectively before stringifying. But it sounds like much effort.
Is there a better way? Thanks.
Converting a Map to a simple Object won't work, unless the keys happen to be all strings. Remember that Maps can have anything as keys, including things that are not equal by reference but that will result in the same string when coerced.
The obvious way to serialise Maps and Sets would be, indeed, to convert them to arrays and then serialise that. Fortunately, you don't have to write much code yourself, just use the built-in iterators:
const map = new Map([['a', 1], ['b', 2]])
const set = new Set([2,3,5,7,11])
serializedMap = JSON.stringify([...map]) // => [["a", 1], ["b", 2]]
serializedSet = JSON.stringify([...set]) // => [2,3,5,7,11]
Now, as you noticed, you'll have some trouble with nested Maps and Sets. To avoid writing specific code to dive into objects and turn their deeply nested Map and Set values into arrays, you can define the toJSON method on Map and Set prototypes. The method is called implicitly by JSON.stringify:
Map.prototype.toJSON = function () {
return [...this]
}
// or, if you really want to use objects:
Map.prototype.toJSON = function () {
var obj = {}
for(let [key, value] of this)
obj[key] = value
return obj
}
// and for Sets:
Set.prototype.toJSON = function () {
return [...this]
}
Be careful, toJSON may be defined for Sets and Maps sometime in the future, with a different behaviour than this. Although it seems unlikely.
Now, whenever you call JSON.stringify and it sees a Map or Set, it will notice the toJSON method and use it instead of just copying the object's properties.
Another, similar and more ECMA-approved solution is to use JSON.stringify's second argument. Define a helper function that will preprocess values, replacing Maps and Sets with appropriate arrays or objects:
function mapReplacer(key, value) {
if(value instanceof Map || value instanceof Set) {
return [...value]
// of course you can separate cases to turn Maps into objects
}
return value
}
Now just pass mapReplacer to JSON.stringify:
JSON.stringify(map, mapReplacer)

array.indexOf in TypeScript/JavaScript [duplicate]

This question already has answers here:
indexOf method in an object array?
(29 answers)
Closed 7 years ago.
UPDATE: Although this question is marked as duplicated with this. But #ssube's way is neat and much smarter.
UPDATE2: Seems there is new way to do it in the comment by #Grungondola.
I am using Typescript.
This works well.
var array1 = [];
array1.push(5);
array1.push(6);
console.log("a", array2.indexOf(6));
But this does not work well. Because array2.indexOf returns -1 which means it does not find it.
var array2 = [];
array2.push({aa:5,bb:5});
array2.push({aa:6,bb:6});
console.log(array2.indexOf({aa:6,bb:6}));
Looks like indexOf does not support Object. Does TypeScript have its own ways to deal with this kind of problem? Thanks.
No. The problem is not with Object, but that you are creating two different objects.
The object literal syntax ({foo: 'bar'}) declares an object inline. When the script is executed, the object is created. Using that syntax multiple times creates multiple objects.
You can easily test that with {foo: 3} === {foo: 3}. This will evaluate to false, but they are not the same object (reference).
The indexOf method checks if the object, string, number, etc, is present in the array. You're passing a new object, which is not in the array.
If you have a reference to the object, you can use that and indexOf will work:
var foo = {aa:5,bb:5}, bar = {aa:6,bb:6};
var array2 = [];
array2.push(foo);
array2.push(bar);
console.log(array2.indexOf(foo));
Because you're referring to the same instance, this will print the index.
You can also use filter or find with a predicate to perform a deep search:
function deepIndexOf(arr, obj) {
return arr.findIndex(function (cur) {
return Object.keys(obj).every(function (key) {
return obj[key] === cur[key];
});
});
}
var array2 = [];
array2.push(foo);
array2.push(bar);
console.log(deepIndexOf(array2, foo));
This won't recurse into nested objects, but will accomplish the comparison you're looking for (equivalence on two objects and their immediate fields).

Categories

Resources