Related
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
Consider the following code:
a = {};
a['a'] = 1;
a['b'] = 3;
a['c'] = 5;
a['d'] = 2;
b = [];
b['a'] = 1;
b['b'] = 3;
b['c'] = 5;
b['d'] = 2;
console.log(typeof a, typeof b);
console.log(a);
console.log(b);
And the output console.log is as follow:
object object
{ a: 1, b: 3, c: 5, d: 2 }
[ a: 1, b: 3, c: 5, d: 2 ]
Granted that they are of both type object, to my understanding, a is an object of key:value pairs, where as b is an array of [key:value] pairs. Is that the right assumption?
And aside from a for(index in array) pattern of looping, where I have to use the iterator index to grab the value (array[index]), is there a better, more simple method of looping through these key/value pairs to grab the index/value or to just process the data at each index, sort of like map or reduce for arrays?
One such problem I ran into was, if I wanted to grab the key with the least count (in the above examples, it would be 'a'), is there a way to do it without using the for( index in array) pattern?
In Javascript arrays are objects, so you can add keys like you are doing in your example above, but this is not the normal way to use arrays and will probably confuse a lot of people looking at your code. There are occasional when it's useful to add a property to an array, but using them just as key stores definitely not the norm.
If you want an ordered collections with numeric indexes, use an array:
let arr = []
arr.push(10)
arr.push(20)
console.log(arr)
console.log(arr[0])
If you want properties where order isn't important and you will have string keys, use an object. In the above example you are using both types like objects — there's no reason to use an array like that.
Having said that if you want an iterator of values from an object, you can use Object.values(). This delivers an array of values from an object if you only want to process the values:
a = {};
a['a'] = 1;
a['b'] = 3;
a['c'] = 5;
a['d'] = 2;
console.log(Object.values(a))
// process them
let newArr = Object.values(a).map(n => n * 10)
console.log(newArr)
Object.entries is also useful — it returns an array of key/value pairs, which, with reduce() makes a nice way to find the minimum:
a = {
a: 1,
b: 3,
c: 5,
d: 2
};
// current will be an array of the form [key, value]
let [minKey, minValue] = Object.entries(a).reduce((min, current) => min[1] < current[1] ? min : current)
console.log(minKey, minValue)
I need for some debugging to see the original order of one JavaScript object's properties but (at least in chrome devtools) console.log() shows me an alphabetically ordered object.
Ex:
var obj = {
z: 1,
t: 2,
y: 3,
a: 4,
n: 5,
k: 6
}
console.log(obj) shows this:
Object {z: 1, t: 2, y: 3, a: 4, n: 5…}
a:4
k:6
n:5
t:2
y:3
z:1
//expected (needed ) original order
z: 1
t: 2
y: 3
a: 4
n: 5
k: 6
console.log does indeed sort the properties, in some cases you can use JSON.stringify which preserves the order, e.g.
console.log(JSON.stringify(obj, null /*replacer function */, 4 /* space */))
NB: contrary to the popular belief, js objects maintain the enumeration order, as per the OwnPropertyKeys specification (integers first, then other properties in insertion order)
Objects do retain the order that their (non-numeric) keys were inserted in, but they are only guaranteed to iterate in that order using certain methods. Per the specification, Object.keys and its variants, JSON.stringify, and for..in loops all iterate in an unspecified order. These methods all call EnumerateObjectProperties, which explicitly states:
The mechanics and order of enumerating the properties is not specified
While environments generally iterate in the predictable order anyway for those methods, that behavior is in no way guaranteed by the specification.
But, Object.getOwnPropertyNames (and Reflect.ownKeys, and Object.getOwnPropertySymbols) are guaranteed to iterate in a particular order: ascending numeric keys, followed by other keys in insertion order, per [[OwnPropertyKeys]].
So, a specification-guaranteed method of logging (non-numeric) properties in insertion order will involve using one of the above methods, rather than Object.keys or its variants:
var obj = {
z: 1,
t: 2,
y: 3,
a: 4,
n: 5,
k: 6
};
const str = '{\n' +
Object.getOwnPropertyNames(obj).map(key => ` ${key}: ${obj[key]}`).join('\n')
+ '\n}';
console.log(str);
For nested objects, you'll need a recursive function instead:
var obj = {
z: 1,
t: 2,
y: 3,
a: 4,
nested: {
foo: 9,
bar: 99,
baz: 35
},
n: 5,
k: 6
};
const objToArrOfLines = (obj, lines=[], leftPadding=0, keyForThisObj) => {
lines.push(`${' '.repeat(leftPadding)}${keyForThisObj ? keyForThisObj + ': ' : ''}{`);
Object.getOwnPropertyNames(obj).forEach((key) => {
const val = obj[key];
if (typeof val === 'object') {
objToArrOfLines(val, lines, leftPadding + 2, key);
} else {
lines.push(`${' '.repeat(leftPadding + 2)}${key}: ${val}`);
}
});
lines.push(`${' '.repeat(leftPadding)}}`);
return lines;
};
const objToStr = (obj) => {
console.log(objToArrOfLines(obj).join('\n'));
};
objToStr(obj);
Another easy solution would be:
console.log(Object.entries(obj).map(k=>({[k[0]]:k[1]})))
The output of console.table(yourObj) outputs in the order you'd expect it to, though as a table.
Caveat: Objects keys that are numerical (only numbers, even if wrapped within quotes) are ALWAYS sorted from least to most in modern browsers, however, as soon as a non-numerical character is met in the key it ignores that key from being sorted. There are hackish workarounds that take advantage of this fact like prepending all keys with _ or some other non-numerical character to prevent the objects from being auto-sorted. See https://www.stefanjudis.com/today-i-learned/property-order-is-predictable-in-javascript-objects-since-es2015/ for more
If you need to log a very big object, to be able to collapse keys, another option would be to transform it to key-value pair arrays.
let keepKeyOrder = function(obj) {
if (typeof obj === 'object' && !Array.isArray(obj)) {
let transformKey = (k) => [k, keepKeyOrder(obj[k])];
return Object.keys(obj).map(transformKey);
} else {
return obj;
}
};
console.log(keepKeyOrder({a:3,c:4,b:{b3:123,b2:234,b1:345}}));
Outputs:
Just to clarify Console.log does sort but it also doesn't....
This is a list of breakpoints, the correct order (as it was created) is default, mobileM, mobileL, tablet, desktopM and that's what's show in the first line.
But when you expand it they're in alphabetical order. Same if you hover over something and view the popup.
The point being one minute it does sort them and the next it doesn't. Really ought to be an option, but doesn't seem to be - and this mixed behavior can really trip you up if you're normally not concerned about property order.
I have two JSON arrays like
array1=[{a:1,b:2,c:3,d:4}]
&
array2=[{a:2,b:5,c:3,d:4}]
Is there any method to find the value of one of the keys in array1 present in array 2.Here in the array 1 key b contains the value 2,and array2 also contain a key a with value 2. How can I capture the key name of array 2 which has the same value for one of the keys in array.
I don't quite understand if you are interested in operating on arrays or objects - as your example is a pair of single element arrays, and the comparison is clearly between the objects in the arrays.
That said, if your goal is to compare two objects, and return the set of keys that are the same for both, you would do something like
obj1 = {a:1,b:2,c:3,d:4};
obj2 = {a:2,b:5,c:3,d:4};
function sameKeys(a,b) {
return Object.keys(a).filter(function(key) {
return a[key] === b[key];
});
}
console.log(sameKeys(obj1, obj2));
When I run this, I get:
[ 'c', 'd' ]
I hope that is what you were asking...
Wrote a prototype function to compare an object against another.
var obj1 = {a: 1, b: 2, c: 3, d: 4};
var obj2 = {a: 2, b: 4, c: 100, d: 200}
Object.prototype.propertiesOf = function(visitorObj) {
result = {};
//Go through the host object
for (thisKey in this) {
//Exclude this function
if (!this.hasOwnProperty(thisKey))
continue;
//Compare the visitor object against the current property
for (visitorKey in visitorObj) {
if (visitorObj[visitorKey] === this[thisKey])
result[visitorKey] = thisKey;
}
}
return result;
}
console.log(obj1.propertiesOf(obj2));
Simply call the propertiesOf function of any object by passing another object as the argument. It returns an object which has similar keys linked to each other.
The above example will result in:
{a: "b", b: "d"}
It seems you want something like this: make a function that finds all the keys in the 2nd object that have a given value. Then pass the value from the first object to that function.
obj1={a:1,b:2,c:3,d:4};
obj2={a:2,b:5,c:3,d:4};
function findKeysByValue(obj, v) {
var results = [];
for (var k in obj) {
if (obj.hasOwnProperty(k) && v == obj[k]) {
results.push(k);
}
}
return results;
}
console.log(findKeysByValue(obj2, obj1['b'])); // ['a']
According to the MDN JavaScript documentation you can define object literal property names using integers:
Additionally, you can use a numeric or string literal for the name of a property.
Like so:
me = {
name: "Robert Rocha",
123: 26,
origin: "Mexico"
}
My question is, how do you reference the property that has an integer as a name? I tried the usual me.123 but got an error. The only workaround that I can think of is using a for-in loop. Any suggestions?
You can reference the object's properties as you would an array and use either me[123] or me["123"]
Dot notation only works with property names that are valid identifiers. An identifier must start with a letter, $, _ or unicode escape sequence. For all other property names, you must use bracket notation.
In an object literal, the property name must be an identifier name, string literal or numeric literal (which will be converted to a string since property names must be strings):
var obj = {1:1, foo:'foo', '+=+':'+=+'};
alert(obj[1] + ' ' + obj.foo + ' ' + obj['+=+']); // 1 foo +=+
You can use me[123] or me["123"]. Both work.
You can use bracket notation me[123].
Just in case anyone else was confused by this: using integer (rather than string) property names may give slightly different - though functionally the same - results (depending on the browser) when you have objects within objects.
Simple objects with no nested objects have consistent behavior across browsers (though as the accepted answer says, we need to use brackets instead of dots to access integer property names):
var str_simple = {
a: "b", c: "d", e: "f", g: "h",
};
str_simple.a === "b"; // true
str_simple.e === "f"; // true
var int_simple = {
1: 2, 3: 4, 5: 6, 7: 8,
};
int_simple[1] === 2; // true - must use brackets instead of dots
int_simple[5] === 6; // true
// this works b/c int property names are coerced to strings anyway
int_simple[1] === int_simple['1']; // true
And this nested object with string keys works exactly as expected:
var str_nested = {
a: {b: "c"},
d: {e: "f", g: "h"},
};
str_nested.a; // returns object as expected, no matter the browser - {b: "c"}
str_nested.a.b === "c"; // true
str_nested.d.g === "h"; // true
But this equivalent nested object with integer keys returns slightly different results depending on the browser, though you can still access the nested objects in the same way (so functionally, it still works the same):
var int_nested = {
1: {2: 3},
4: {5: 6, 7: 8},
};
// latest Chrome (57)
// Safari 10 (latest for my Mac, 10.10 Yosemite)
int_nested[1]; // returns object as expected - {2: 3}
int_nested[1][2] === 3; // true
// latest Firefox (52)
int_nested[1]; // RETURNS ARRAY-LIKE OBJECT - Object [ <2 empty slots>, 3 ]
int_nested.length; // undefined because it's not technically an array
int_nested[1][2] === 3; // true - works b/c object was padded with empty slots
// and again, in all browsers, we can exchange the integer keys
// for equivalent strings since property names are coerced to strings anyway
int_nested[1][2] === int_nested['1'][2];
int_nested['1'][2] === int_nested[1]['2'];
int_nested[1]['2'] === int_nested['1']['2'];
This behavior will still be slightly different but functionally the same if you programmatically construct a nested object. For example, say we wanted to write a function that would take a list of pairs (e.g. [[0, 0], [0, 1], [1, 2], [2, 3]]) and convert it into a nested object so we could check if the pair is in the object with O(1) time (e.g. {0: {0: true, 1: true}, 1: {2: true}, 2: {3, true}}). Note that Sets check reference equality and not value equality, so we couldn't store the pair itself in the Set and achieve the same results:
// [[0, 0], [0, 1], [1, 2], [2, 3]] ->
// {
// 0: {0: true, 1: true},
// 1: {2: true},
// 2: {3: true},
// }
function createNestedObject(pairs) {
var obj = {};
for (var pair of pairs) {
var x = pair[0], y = pair[1];
// must create outer object for each unique x or else
// obj[x][y] would fail b/c obj[x] would be undefined
if (!obj.hasOwnProperty(x)) {
obj[x] = {};
}
obj[x][y] = true;
}
return obj;
}
function exists(nested, pair) {
var x = pair[0], y = pair[1];
// uses !! operator so if pair isn't in nested
// we return false instead of undefined
return !!(nested[x] && nested[x][y]);
}
Pairs with strings will work as expected:
var pairs = [["a", "a"], ["a", "b"], ["c", "d"], ["d", "e"]];
var nested = createNestedObject(pairs);
nested; // as expected - {a: {a: true, b: true}, c: {d: true}, d: {e: true}}
exists(nested, ["a", "a"]); // true
exists(nested, ["a", "b"]); // true
exists(nested, ["ZZZ", "ZZZ"]); // false
But in certain browsers, integer pairs will be different but functionally the same:
var pairs = [[0, 0], [0, 1], [1, 2], [2, 3]];
var nested = createNestedObject(pairs);
nested; // in Safari 10/Chrome 57 - returns nested objects as expected
nested; // in Firefox 52 - Object [ Object[2], Object[3], Object[4] ]
// BUT still gives correct results no matter the browser
exists(nested, [0, 0]); // true
exists(nested, [0, 1]); // true
exists(nested, ['0', '0']); // true
exists(nested, [999, 999]); // false
The situation with numeric property names seems more complicated than it is explained in the answers so far. It is true that you can access such properties via for-in loop. However, it might be important to know that for-in loop gives keys as strings, not as numbers as you might expect:
var obj = {1:2};
for (var key in obj) {
alert(typeof(obj[key])); // you get "number" as expected, however
alert(typeof(key)); // you get "string", not "number"
}
A similar thing happens during serialization with JSON:
JSON.stringify( {1:2} ) === '{"1":2}'
So if you code depends on this little detail you better be aware of it.