Boolean comparison explanation [duplicate] - javascript

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

Related

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

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

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.

JavaScript arrays: string indexed items

I've had a bit of a wakeup to the nature of JavaScript array indexes recently. Pursuing it, I found the following (I'm working with Node.js in interpretive mode here):
var x=[];
x['a']='a';
console.log(x); // Yields [ a: 'a' ]
console.log(x.length); // yields 0 not 1
x[1]=1;
console.log(x); // Yields [ , 1, a: 'a' ]
console.log(x.length); // Yields 2 not 3 (one for empty 0 space, one for the occupied 1 space)
Is a: 'a' really what it looks like - an object property embedded in an array - and, thus, isn't counted in the array property .length?
In JavaScript, arrays are just objects with some special properties, such as an automatic length property, and some methods attached (such as sort, pop, join, etc.). Indeed, a will not be counted in your array, since the length property of an array only stores the amount of elements with a property name that can be represented with a 32-bit positive integer.
And since arrays always automatically define every numbered element up to the highest element with a positive 32-bit int property name, this effectively means the length property stores 1 higher than the element with the highest 32-bit integer as a property name. Thanks #Felix Kling for correcting me about this in the comments.
Adding properties such as a is not forbidden at all, but you should watch out with them, since it might be confusing when reading your code.
There's also a difference in walking through the elements in the array:
To walk through all the numbered elements:
for (var i=0; i<myArray.length; i++) {
//do something
}
To walk through every property that's not built-in:
for (var i in myArray) {
//do something
}
Note that this loop will also include anything that's included from Array.prototype that's not a built-in method. So, if you were to add Array.prototype.sum = function() {/*...*/};, it will also be looped through.
To find out if the object you're using is indeed an array, and not just an object, you could perform the following test:
if (Object.prototype.toString.call(myObject) === '[object Array]') {
//myObject is an array
} else if (typeof myObject === 'object') {
//myObject is some other kind of object
}
See #artem's comment: myObject instanceof Array might not always work correctly.
That's correct, and it's a good illustration of the fact that that new Array you've created is really just a special kind of Object. In fact, typeof [] is 'object'! Setting a named property on it (which might be written more cleanly here as x.a = 'a') is really setting a new property to the object wrapper around your "real" array (numbered properties, really). They don't affect the length property for the same reason that the Array.isArray method doesn't.
Yes, that's correct. This works because pretty much everything in JavaScript is an object, including arrays and functions, which means that you can add your own arbitrary string properties. That's not you say that you should do this.
Arrays ([]) should be indexed using nonnegative integers. Objects ({}) should be "indexed" using strings.

Typed Arrays and normal Arrays

Since typed arrays are just normal Arrays with typed values, they should also be considered "Arrays". But when I test if typed arrays are arrays, it returns false. For example:
var arr = new Int32Array(3);
Array.isArray(arr); //false
Does Array.isArray only check if arr is exactly a type Array object? How can I test if arr is an array, regardless of typed or not typed?
Typed Arrays are very different beasts than arrays. Arrays are generic collections, with loads of helper-methods for operating on those collections.
Typed Arrays were put in primarily to help with the browser version of OpenGL (though they obviously have other uses).
Try calling typedInt8Array.forEach()...
They're not the same thing.
So if you want a function which checks whether a variable/property is an array, or a typed array, then you will have to manually check the instanceof for each of the Int8Array|Int16Array|etc....
I'm partial to this trick:
obj.length === +obj.length; // true if it's an array
Since JavaScript Arrays have the special length property this is a pretty safe way to tell unless you have objects that you've defined a length property on manually.
Since Typed Arrays still have a length property, the old trick
obj.length === +obj.length
still works.
That means that length is a numeric property ( +v transforms a var to number, so if it's still === after the transform, then it was a number to begin with).
This is a solution I came up with:
isArray = /Array/.test(arr.constructor.name);

Memory management of JavaScript Array [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Are Javascript arrays sparse?
I am learning JavaScript at the moment and have been reading some simple introductions and tutorials. While looking at the Array object I stumbled upon some details, which strike me as very odd, coming from other languages like C/Java/Scala/...
So lets assume we define an array as such:
var arr = ['foo','bar','qux']
We now assign
arr[5] = 'baz'
which results in our array looking like this:
arr
>> ["foo", "bar", "qux", undefined, undefined, "baz"]
And the length is as expected
arr.length
>> 6
JavaScript has kindly expanded our array to the needed length - six - and the new items are set to undefined - except for the one we actually assigned a value to.
From a low level point of view this is horrible memory-wise. Typically an array would be a continuous range in memory - making an array bigger generally involves copying the whole array to a new memory location, sufficient in size. This is a very costly operation.
Now, I do realize that this is likely not what JavaScript engines are doing, as copying around arrays would be crazy expensive and the memory space would be wasted on all these 'undefined' values.
Can someone tell me what actually happens behind the door?
Are arrays actually some sort linked lists?
Are the 'undefined' array items actually there?
How expensive is it to work with large arrays that are mostly filled with 'undefined'?
In the first version of JavaScript, there were no arrays. They were later introduced as a sub-class of that "mother of all objects": Object. You can test this quite easily by doing this:
var foo = [1,2,3,4];
for (var n in foo)
{//check if n is equal (value and type) to itself, coerced to a number
console.log(n === +(n) ? 'Number' : 'String');
}
This will log String, time and time again. Internally, all numeric keys are converted to strings. The Length property merely fetches the highest index, and adds 1 to it. Nothing more. When you display your array, the object is iterated, and for each key, the same rules apply as for any object: first the instance is scanned, then the prototype(s)... so if we alter our code a bit:
var foo = [1,2,3,4];
foo[9] = 5;
for (var n in foo)
{
if (foo.hasOwnProperty(n))
{//check if current key is an array property
console.log(n === +(n) ? 'Number' : 'String');
}
}
You'll notice the array only has 5 own properties, the undefined keys 4-8 are undefined, because there was no corresponding value found within the instance, nor in any of the underlying prototypes. In short: Arrays aren't really arrays, but objects that behave similarly.
As Tim remarked, you can have an array instance with an undefined property that does exist within that object:
var foo = [1,2,undefined,3];
console.log(foo[2] === undefined);//true
console.log(foo[99] === undefined);//true
But again, there is a difference:
console.log((foo.hasOwnProperty('2') && foo[2] === undefined));//true
console.log((foo.hasOwnProperty('99') && foo[99] === undefined));//false
RECAP, your three main questions:
Arrays are objects, that allow you to reference their properties with numeric instances
The undefined values are not there, they're merely the default return value when JS scans an object and the prototypes and can't find what you're looking for: "Sorry, what you ask me is undefined in my book." is what it says.
Working with largely undefined arrays doesn't affect the size of the object itself, but accessing an undefined key might be very, very marginally slower, because the prototypes have to be scanned, too.
Update:
Just quoting the Ecma std:
15.4 Array Objects
Array objects give special treatment to a certain class of property names. A property name P (in the form of a
String value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to
2^32
1. A property whose property name is an array index is also called an element. Every Array object has a
length property whose value is always a nonnegative integer less than 2^32. The value of the length
property is numerically greater than the name of every property whose name is an array index; whenever a
property of an Array object is created or changed, other properties are adjusted as necessary to maintain this
invariant. Specifically, whenever a property is added whose name is an array index, the length property is
changed, if necessary, to be one more than the numeric value of that array index; and whenever the length
property is changed, every property whose name is an array index whose value is not smaller than the new
length is automatically deleted. This constraint applies only to own properties of an Array object and is
unaffected by length or array index properties that may be inherited from its prototypes.
An object, O, is said to be sparse if the following algorithm returns true:
1. Let len be the result of calling the [[Get]] internal method of O with argument "length".
2. For each integer i in the range 0≤i
a. Let elem be the result of calling the [[GetOwnProperty]] internal method of O with argument
ToString(i).
b. If elem is undefined, return true.
3. Return false.
Arrays are just an ordered list of objects. In JavaScript everything is an object, so arrays are not really arrays as we know them :)
You can find little internals here.
For your doubts about working with large arrays... Well, remember that the less calculation you make "client-side", the faster will be your page.
Answers:
An array in JavaScript is just the same as an object (i.e. an unordered collection of properties) with a magic length property and extra prototype methods (push() etc.)
No, the undefined items are not there. JavaScript has an in operator that test for the existence of a property that you can use to prove this. So for the following array: var arr = ['foo']; arr[2] = 'bar';, 2 in arr returns true and 1 in arr returns false.
A sparse array should take up no more memory than a dense array whose length is the number of properties actually defined in your sparse array. It will only be more expensive to work with a sparse array when you iterate over its undefined properties.
Most javascript implementations implement arrays as some flavor of binary tree or hash table with the array index as the key, so a large range of undefined objects does not use up any memory.
I was told that the arrays come in 2 parts, [value, pointer]. So the pointer of arr[2] is null. When you add a 5 it changes the address from null to point to number 3, which points to number 4, which points to number 5, which is null (so end of array).
Im not sure how true this is as ive never actually checked it. But it seems to make sense.
So you cant do the maths like on a c type array (ie to get to value 4 just do starting memory point + 4x (object amount in memory)) but you can do it by following the array peice by peice

Categories

Resources