Object comparison Complexity Javascript - javascript

I would like to compare the performances of comparing two objects in JavaScript having a unique id 'key' using their key or equality sign i.e: object1 === object2 or object1.key === object2.key.
While both are working I am wondering is it better to compare directly the keys as it will have only two values to compare or is Javascipt comparing each key / value in each value and then the complexity would be O(n) (n being the muber of keys of an object)
Is it comparing addresses or something to make it efficient?

Case 1. comparing distinct objects:
While both are working
Well they don't :
{a: 1} == {a: 1} // false
you can't compare two distinct objects by two (or three) equal sign. you have two way:
JSON.stringify({a: 1}) === JSON.stringify({a: 1})
Or:
{key: 1, a: 1}.key === {key: 1, a: 1}.key
Which obviously the second one is more efficient.
Case 2. Comparing non-distinct objects like:
var obj1 = {key: 1, a: 1, b: 2}
var obj2 = {key: 2, a: 1, b: 2}
var obj3 = obj1
obj3 === obj1 // true
There is no difference between comparing them directly or comparing them by your keys, both are O(1) since js will compare them by refrence, not by comparing their key-values.

Related

Why object merges properties but array does not merges values

Can someone tell why object merges values but array does not
See the code block below:
const a = {'a': 1, 'b': 2}
const b = {'b': 4, 'c': 3}
console.log({...a, ...b})
This Outputs
{ a: 1, b: 4, c: 3 }
But when I use the code below:
const c = [1,2]
const d = [2,3]
console.log([...c, ...d])
This outputs
[ 1, 2, 2, 3 ]
Why object merges properties...
It doesn't merge properties, it merges objects. Notice the value of b in your result: It's 4 (the value from the b object), not some merged value of 2 (from the a object) and 4 (from the b object). Each property from each source object is just copied into the target object (with later objects overwriting properties from earlier objects), the properties themselves are not merged together.
But fundamentally, object property spread and iterable spread are just completely different things with different purposes and different semantics, because objects and arrays are different beasts (at least conceptually; arrays actually are objects in JavaScript). Properties have names which are an intrinsic part of the property. Array elements just have indexes, and it's normal for values to be moved around an array (moved to different indexes). The two different definitions of spread are each useful for the data type they're defined for.
If you want to treat an array like an object, though, you can since arrays are objects in JavaScript. (Although in this case it isn't useful.) Here's an example (I've changed c's element values so it's clear what's coming from where):
const c = ["a", "b"];
const d = [2, 3];
console.log(Object.assign([], c, d));
In that case, since d has values for both indexes 0 and 1, none of c's elements are in the result. But:
const c = ["a", "b", "c", "d", "e"];
const d = [2, 3];
console.log(Object.assign([], c, d));
Short answer
When using the spread operator, Regular Objects are ASSIGNED.
When using the spread operator, Arrays are CONCATENATED.
Long Answer
I believe the source of your confusion is that every array in JavaScript is just an object belonging to the Array constructor. So why doesn't joining two or more arrays with the spread operator work the same way as objects do?
Let's analyze what is happening in case of the Object
const a = {'a': 1, 'b': 2};
const b = {'b': 4, 'c': 3};
console.log({...a, ...b}); // Output: { a: 1, b: 4, c: 3 }
console.log(Object.assign({}, a, b)); // Output: { a: 1, b: 4, c: 3 }
console.log({...b, ...a}); // Output: { a: 1, b: 2, c: 3 }
console.log(Object.assign({}, b, a)); // Output: { a: 1, b: 2, c: 3 }
An object is a data structure holding key:value pairs.
Object assignment overwrites the keys with the latest values.
The key b occurs in more than one object and is overwritten with it's latest value. As you can see, if you change the order of the objects spread/assigned, the resulting value of the value of b changes based on the latest object having b.
Now let's come to the Array.
const c = [1,2];
const d = [2,3];
console.log([...c, ...d]); // Output: [ 1, 2, 2, 3 ]
console.log(c.concat(d)); // Output: [ 1, 2, 2, 3 ]
console.log(Object.assign({}, c, d)); // Output: { '0': 2, '1': 3 }
console.log(Object.values(Object.assign({}, c, d))); // Output: [ 2, 3 ]
An array is an object created with the Array constructor which outputs the array as a collection of the values assigned to its keys.
Array concatenation simply joins the arrays.
As you can see above, Object.assign still works on an array because the array is technically an object and it behaves exactly how Object.assign is supposed to work. The keys in this case are simply what we call "index" in an array. This is why when you do array[index] it returns the value, it's the same as object[key] that returns a value. If keys exist, the Object.assign replaces the keys/index with the latest values, else it adds the key-value pair to the object.
Conclusion:
Thus, the difference is how the spread operator works for objects and arrays.
In Objects, spread does Object.assign.
In Arrays, spread does Array concatenation => arrayA.concat(arrayB, arrayC, ...)
Bonus: Set
However, if you want the array to return only unique values, you have to use the Set data structure.
const c = [1,2];
const d = [2,3];
console.log([...new Set([...c, ...d])]); // Output: [1, 2, 3]
console.log(Array.from(new Set(a.concat(b)))); // Output: [1, 2, 3]

String to Integer Object hashing in JavaScript

There are multiple questions here in SO that are similar to my question, but none really answers it completely.
Basically, I want to have objects as keys in JavaScript. I know it is possible with Map, however, it doesn't fully apply to my use-case. Here is the reason:
Let's say I have two objects, var d1 = {a: 1, b: 2} and var d2 = {a: 1, b: 2}, and a Map var m = new Map(). When adding d1 to m, when I call m.get(d2) I will get undefined. I assume it is because Map works in a reference-like manner.
However, I want the following functionality:
m.set(d1, 'hit');
m.get(d2) // should return 'hit' because properties and values of d1 = d2
One approach I thought of was not to use Map, but a simple JS object. Keys would be JSON.stringify(obj) and when I want to get a value from the object, I use JSON.parse(key) and then perform object equality check (Object.getOwnPropertyNames and then checking one-by-one).
However, I feel that this approach is way more complex and time-consuming than it should be. What I am looking for is probably a hash function of some sort that would efficiently map a object with keys of type String to values of type Number (integer in my case). Also, the ordering can be different (d1 = {a: 1, b: 2} should equal d2 = {b: 2, a: 1}).
How to design an efficient hash function to work with either JS Objects/Maps to perform the above-mentioned operations?
Write a function that turns the object into a string with the keys in a consistent order.
function objToKey(obj) {
Object.keys(obj).sort().map(k => `${k}:${obj[k]}`).join(',');
}
var d1 = {a: 1, b: 2},
d2 = {b: 2, a: 1},
m = new Map();
m.set(objToKey(d1), "foo");
console.log(m.get(objToKey(d2)));

Why does {. . . .0} evaluate to {}?

I just found {....0} in friend's code. Evaluating it in console returns {} (empty object).
Why is that? What is the meaning of 4 dots in JavaScript?
Four dots actually have no meaning. ... is the spread operator, and .0 is short for 0.0.
Spreading 0 (or any number) into an object yields an empty object, therefore {}.
Three dots in an object literal are a spread property, e.g.:
const a = { b: 1, c: 1 };
const d = { ...a, e: 1 }; // { b: 1, c: 1, e: 1 }
The last dot with a 0 is a number literal .0 is the same as 0.0. Therefore this:
{ ...(0.0) }
spreads all properties of the number object into the object, however as numbers don't have any (own) properties you get back an empty object.
In a simple terms {...} spread operator in javascript extends one object/array with another.
So, when babelifier tries extending one with another, it has to identify whether it is trying to extend an array or an object.
In the case of array, it iterates over elements.
In the case of object, it iterates over keys.
In this scenario, the babelyfier is trying to extract keys for number by checking the Object's own property call which is missing for number so it returns empty Object.
Spread operator {...} allows iterables to expand. It means that those data types that can be defined in form of key-value pairs can be expanded. In terms of Object we call key-value pair as Object property and it's value whereas in terms of arrays we can think index as key and element in array as it's value.
let obj = { a: 4, b: 1};
let obj2 = { ...obj, c: 2, d: 4}; // {a: 4, b: 1, c: 2, d: 4}
let arr1 = ['1', '2'];
let obj3 = { ...arr1, ...['3']}; // {0: "3", 1: "2"}
In terms of array, as it takes index as key so here it replaces element '1' of arr1 with '3' because both of them have same index in different array.
With strings too spread operator returns non-empty object. As string is an array of character so it treats string as an array.
let obj4 = {...'hi',...'hello'} // {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}
let obj5 = {...'y',...'x'} // {0: "x" }
But with other primitive data types it return empty object
with Numbers
let obj6 = { ...0.0, ...55} // {}
with Boolean
let obj7 = { ...true, ...false} // {}
In conclusion those data types that can be treated in form of key-value pairs when used with spread operator {...} returns non-empty object otherwise it returns empty object {}

Javascript Object.keys issue

When I run the following code:
var object1 = {
a: 1,
b: 2
};
var object2 = {
b: 4,
c: 3
};
var obj1Keys = Object.keys(object1);
var obj2Keys = Object.keys(object2);
console.log(obj2Keys[0] in obj1Keys);
The console logs false. Clearly both object1 and object2 have property 'b'. What am I doing wrong?
You need to use the includes() method instead.
console.log(obj1Keys.includes(obj2Keys[0]));
This is because the in operator works on object keys. The "keys" to an array are number indices (0, 1, etc), not their values
You need to take the object, not the array with the keys for checking and in operator. This check the keys of the object and arrays have indices and length property.
If you like to take the array with the keys, cou could use Array#includes.
var object1 = { a: 1, b: 2 },
object2 = { b: 4, c: 3 },
obj1Keys = Object.keys(object1),
obj2Keys = Object.keys(object2);
console.log(obj2Keys[0] in object1);
console.log(obj1Keys.includes(obj2Keys[0]));
By asking if obj2Keys[0] in obj1Keys, you're asking whether 'b' is a key of ['a', 'b']. It's not. It's a value of ['a', 'b']. You could check like this instead:
obj1Keys.includes(objKeys[0])

Any modern way to disable sort in Object.assign if objects has numeric keys?

For example: snippet img
var a = {1: '1', 2: '2'}
var b = {3: '3', 4: '4'}
Object.assign({}, a, b)
> {1: "1", 2: "2", 3: "3", 4: "4"}
Object.assign({}, b, a)
> {1: "1", 2: "2", 3: "3", 4: "4"}
Is there way to disable sorting?
No, because (as you said) of the numeric keys (or to use the spec's term, integer indexes*).
Object.assign works in the order defined by [[OwnPropertyKeys]], which for ordinary objects is the OrdinaryOwnPropertyKeys abstract operation, which lists the properties in a defined order (integer indexes first, then other string-named properties in order of creation, then Symbol-named properties in order of creation). The resulting object's properties will be enumerated (by operations that follow the defined order) in the same way, and so, integer indexes, numerically, followed by other properties in creationg order.
If your keys weren't integer indexes, you could control the order of the result by pre-creating the properties in the order you wanted them, but not in the case of integer index keys.
So for instance, if your keys were a, b, c, and d, you could determine the order the resulting object's properties were listed (for operations that follow the order):
const x = {a: 'a', b: 'b'};
const y = {c: 'c', d: 'd'};
const result1 = Object.assign({c: null, d: null, a: null, b: null}, x, y);
console.log(JSON.stringify(result1));
const result2 = Object.assign({c: null, d: null, a: null, b: null}, y, x);
console.log(JSON.stringify(result2));
const result3 = Object.assign({a: null, b: null, c: null, d: null}, x, y);
console.log(JSON.stringify(result3));
const result4 = Object.assign({a: null, b: null, c: null, d: null}, y, x);
console.log(JSON.stringify(result4));
Note that I'm using JSON.stringify there for output, because JSON.stringify is defined to follow property order (whereas for-in and Object.keys are not).
But not for your keys, which are integer indexes.
If order is important, usually an object is the wrong data structure; instead, you'd want an array. Arrays are also objects and some of the ordered-ness of an array is just convention (barring optimization), but they have several important order-specific features, not least the length property and their various methods that work in a defined order.
* "integer index" is defined here:
An integer index is a String-valued property key that is a canonical numeric String (see 7.1.16) and whose numeric value is either +0 or a positive integer ≤ 253-1.

Categories