Javascript Object.keys issue - javascript

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])

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]

Why is last element overriding others in map function?

var a = {};
var b = [1,2,3,4];
var c = ['a', 'b', 'c', 'd'];
b.map((i) => {
c.map((j) => {
a[j] = i
})
});
console.log(a);
In above code I am expecting the output to be {a:1, b:2, c:3, d:4}. But It's giving {a:4, b:4, c:4, d:4}. Same is the case with using for loops with let keyword instead of maps. What's the reason behind this and how to fix it to get the desired output?
The issue is because each iteration of your loop overwrites the property values in your object (i.e. a, b, c and d) with the current value of i. Therefore when the loops end, the properties have all been set to the last value of i, which is 4.
To achieve your expected outcome, you don't need the inner map() call. You can access the c array by index to align it with the values from the b array,
In addition, note that map() should used to transform the values of an existing array into a new array. As your logic is not doing this, it's actually using the loop-like behaviour of map() to amend an object, you should use forEach() instead.
const a = {};
const b = [1, 2, 3, 4];
const c = ['a', 'b', 'c', 'd'];
b.forEach((n, index) => {
a[c[index]] = n;
});
console.log(a);
You're essentially doing this:
First it takes the 1 from your "b" array. Then in a loop it is adding each letter from the "c" array to the "a" object and giving it the current "b" value, which is always the same value that loop. So after 1 loop you end with
{ a: 1, b: 1, c:1, d:1 }
Then for the second loop it is essentially mapping again and overwriting the "a" object like this:
{ a: 2, b: 2, c: 2, d:2 }
This goes on until every value is 4.

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 {}

Array destructuring in JavaScript

I have this code in my vue-js app:
methods: {
onSubmit() {
ApiService.post('auth/sign_in', {
email: this.email,
password: this.password,
})
.then((res) => {
saveHeaderToCookie(res.headers);
this.$router.push({ name: 'about' });
})
.catch((res) => {
this.message = res.response.data.errors[0];
this.msgStatus = true;
this.msgType = 'error';
});
},
}
While running Eslint I got an error saying "Use array destructuring" (prefer-destructuring) at this line:
this.message = res.response.data.errors[0];
What is array destructuring and how to do this? Please provide me a concept on this. I've researched it but could not figure it out.
Destucturing is using structure-like syntax on the left-hand-side of an assignment to assign elements of a structure on the right-hand-side to individual variables. For exampple,
let array = [1, 2, 3, 4];
let [first, _, third] = array;
destructures the array [1, 2, 3] and assigns individual elements to first and third (_ being a placeholder, making it skip the second element). Because LHS is shorter than RHS, 4 is also being ignored. It is equivalent to:
let first = array[0];
let third = array[2];
There is also an object destructuring assignment:
let object = {first: 1, second: 2, third: 3, some: 4};
let {first, third, fourth: some} = object;
which is equivalent to
let first = object.first;
let third = object.third;
let fourth = object.some;
Spread operator is also permitted:
let [first, ...rest] = [1, 2, 3];
would assign 1 to first, and [2, 3] to rest.
In your code, it says you could do this instead:
[this.message] = res.response.data.errors;
The documentation on prefer-destructuring lays out what it considers to be "correct".
U can rewrite that line as [this.message] = res.response.data.errors; and that es-lint error will go off. See this example for better understanding
var x = {
y: {
z: {
w: [3, 4]
}
}
};
function foo() {
[this.a] = x.y.z.w
console.log(this.a);
}
foo() // prints 3
For more information about array destructuring please see here
Always look things up on MDN if you want to find out about javascript things. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Array_destructuring
Here's a simple example of destructuring:
const [a, b] = ['a', 'b'];
Its a shorthand available since es6 that allows doing variable assignment in a more shorthand way.
The original way would be like:
const arr = ['a', 'b'];
const a = arr[0];
const b = arr[1];
And the es6 way would be like:
const arr = ['a', 'b'];
const [a, b] = arr;
Now in regards to the eslint error, I actually disagree with that one. Your code by itself should be fine. So you should file an issue on the Eslint github repo to ask about why that line is triggering the "prefer-destructuring" warning.
Beside of the given destructuring assignments, you could take an object destructuring for an array if you like to take certain elements, like the 11th and 15th element of an array.
In this case, you need to use the object property assignment pattern [YDKJS: ES6 & Beyond] with a new variable name, because you can not have variables as numbers.
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
{ 11: a, 15: b } = array;
console.log(a, b);
Destructuring is a method of extracting multiple values from data stored in (possibly nested) objects and Arrays. It can be used in locations that receive data or as the value of objects. We will go through some examples of how to use destructuring:
Array Destructuring
Array destructuring works for all iterable values
const iterable = ['a', 'b'];
const [x, y] = iterable;
// x = 'a'; y = 'b'
Destructuring helps with processing return values
const [all, year, month, day] =
/^(\d\d\d\d)-(\d\d)-(\d\d)$/
.exec('2999-12-31');
Object Destructuring
const obj = { first: 'Jane', last: 'Doe' };
const {first: f, last: l} = obj;
// f = 'Jane'; l = 'Doe'
// {prop} is short for {prop: prop}
const {first, last} = obj;
// first = 'Jane'; last = 'Doe'
Examples of where to use Destructuring
// Variable declarations:
const [x] = ['a'];
let [x] = ['a'];
var [x] = ['a'];
// Assignments:
[x] = ['a'];
// Parameter definitions:
function f([x]) { ··· }
f(['a']);
// OR USE IT IN A FOR-OF loop
const arr = ['a', 'b'];
for (const [index, element] of arr.entries()) {
console.log(index, element);
}
// Output:
// 0 a
// 1 b
Patterns for Destructuring
There are two parties involved in any destructuring
Destructuring Source: The data to be destructured for example the right side of a destructuring assignment.
Destructuring Target: The pattern used for destructuring. For example the left side of a destructuring assignment.
The destructuring target is either one of three patterns:
Assignment target: Usually an assignment target is a variable. But in destructuring assignment you have more options. (e.g. x)
Object pattern: The parts of an object pattern are properties, the property values are again patterns (recursively) (e.g. { first: «pattern», last: «pattern» } )
Array pattern: The parts of an Array pattern are elements, the elements are again patterns (e.g. [ «pattern», «pattern» ])
This means you can nest patterns, arbitrarily deeply:
const obj = { a: [{ foo: 123, bar: 'abc' }, {}], b: true };
const { a: [{foo: f}] } = obj; // f = 123
**How do patterns access the innards of values? **
Object patterns coerce destructuring sources to objects before accessing properties. That means that it works with primitive values. The coercion to object is performed using ToObject() which converts primitive values to wrapper objects and leaves objects untouched. Undefined or Null will throw a type error when encountered. Can use empty object pattern to check whether a value is coercible to an object as seen here:
({} = [true, false]); // OK, Arrays are coercible to objects
({} = 'abc'); // OK, strings are coercible to objects
({} = undefined); // TypeError
({} = null); // TypeError
Array destructuring uses an iterator to get to the elements of a source. Therefore, you can Array-destructure any value that is iterable.
Examples:
// Strings are iterable:
const [x,...y] = 'abc'; // x='a'; y=['b', 'c']
// set value indices
const [x,y] = new Set(['a', 'b']); // x='a'; y='b’;
A value is iterable if it has a method whose key is symbol.iterator that returns an object. Array-destructuring throws a TypeError if the value to be destructured isn't iterable
Example:
let x;
[x] = [true, false]; // OK, Arrays are iterable
[x] = 'abc'; // OK, strings are iterable
[x] = { * [Symbol.iterator]() { yield 1 } }; // OK, iterable
[x] = {}; // TypeError, empty objects are not iterable
[x] = undefined; // TypeError, not iterable
[x] = null; // TypeError, not iterable
// TypeError is thrown even before accessing elements of the iterable which means you can use empty Array pattern [] to check if value is iterable
[] = {}; // TypeError, empty objects are not iterable
[] = undefined; // TypeError, not iterable
[] = null; // TypeError, not iterable
Default values can be set
Default values can be set as a fallback
Example:
const [x=3, y] = []; // x = 3; y = undefined
Undefined triggers default values

Javascript: Using a pre-existent object values as keys os a new one

I created this object:
var keys = {A: 'a', B: 'b' };
Later I tried create this other object:
var values = {keys.A: 1, keys.B: 2};
However I got this in Firefox console:
SyntaxError: missing : after property id
Even when I tried:
var vals = {keys['A']: 1, keys['B']: 2}
I got the same error.
The only way to get a success is if I type this:
var vals= {};
vals[keys.A] = 1;
vals[keys.B] = 2;
So, my question is if there is a more elegant way (similar to the first try) to create an anonymous object using as keys the values from a pre-existent object.
Thanks,
Rafael Afonso
Yes, the more elegant way is to use ES6 syntax:
var values = {[keys.A]: 1, [keys.B]: 2};
The thing in brackets can be any expression, so run wild:
var values = { [createPropName() + "_prop"]: 42 }
This is called "computed (dynamic) property names". See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names.
try this
var keys = {A: 'a', B: 'b' },
foo = {};
for (key in keys) { foo[key] = keys[key] }
In an Object Initialiser, the property name must be one of:
IdentifierName
StringLiteral
NumericLiteral
You can do what you want in ES5 using an Object literal for the keys and an Array literal for the values:
var keys = {A: 'a', B: 'b' };
var values = [1, 2];
var obj = {};
Object.keys(keys).forEach(function(v, i) {
obj[v] = values[i];
});
console.log(JSON.stringify(obj)) // {"A":1,"B":2}
However, that isn't reliable because the object properties may not be returned in the order you expect, so you might get:
{"B":1,"A":2};
To do what you want in ES5 and guarantee the order, an array of keys and an array of values is required, so something like:
var keys = ['a', 'b', 'c'];
var values = [1, 2, 3];
var obj = {};
keys.forEach(function(v, i) {obj[v] = values[i]});
console.log(JSON.stringify(obj)) // {"a":1, "b":2, "c":3}

Categories