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.
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
So I have a structure like that:
Foo: {
A: Array[0],
B: Array[0],
C: Array[1]
}
where [X] is length of the array, but Foo is an object, not an Array, therefore I can't use Array method on it.
How do I get first element (letter in this case) which has length > 0 ?
for (let letter in Foo) {
if (letter.length > 0) {
let match = letter;
}
}
I tried something like this (this is simplified version), but it just returns all properties of Foo.
I’m glad you’re using ES6. In this case you can use Object.keys to get an array of all the object’s keys and Array.prototype.find to find the first element with a specific property:
var obj = {
a: [],
b: [],
c: [
2,
3
],
d: [],
e: [
1
]
};
Object.keys(obj).find(a => obj[a].length > 0); // The letter "c" which contains the first non-empty array.
obj[Object.keys(obj).find(a => obj[a].length > 0)]; // Array [2, 3] itself
Note that there’s no consistent “first” element in an object across implementations.
I have an array of mathematical operators stored as an object.
object {0: "-", 1: "*", 2: "/", 3: "+"} called numOperators
I don't know what order the operators will be in, but they need to be execued in the correct order of *, /, +, and - So when I reassign the above object into another parallel object, I assign the indexes of the above object with the order they need to be executed. Then i have this object:
Object {0: 2, 1: 0, 2: 1, 3: 3} called opArray
What I want to do is iterate through the object (which could be any length) by looking at the values instead of the indexes.
In summary, I want to iterate all of the values in the order 3, 2, 1, 0. Look for values of 3 until those are used up, then look for 2, then 1, then finally go through 0. I haven't been able to come up with an efficient way of doing this. Because mathematical operators need to be done in order, a temporary result value is created and then used for the next iteration. Eventually, they are all combined into a single result.
This is what I was trying last:
var valArray = {0: "3", 1: "8", 2: "4", 3: "8", 4: "2"};
var res=[];//temporary result values ordered by execution
var returnRes=0;
var op=0;
$.each(opArr, function(index, value) {//goes through the values in order
if(value==0){
op = numOperators[index]; //uses the indexes that matches the values
res[index]=operate(valArr[index], valArr[index+1],op);
returnRes=res[index];
console.log(res);
}
if(valuei>0){
op = numOperators[index];
res[index]=operate(res[index-1], valArr[index+1],op);
returnRes=res[index];
console.log(res);
}
});
return(returnRes);
I know I may be going about this the completely wrong way, so I'd appreciate some insight on what is an easier way to do this. Thanks!
To clarify further, I have a valid reason for taking this approach to math and not using eval(). Some of the numbers are derived from variables read in as text and converted. There could also be text (non-number text) that needs to be concatenated. So I need to get the math and text separated and evaluated differently. I figured an individual approach would be best.
The apparent solution seems to be using a for each loop. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for_each...in
The other thing you can do and what looks closer to your use case is to write an iterator for this. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
You could make an array and sort it by value:
var opArray = {0: 2, 1: 0, 2: 1, 3: 3};
var array = [];
for (var key in opArray) {
array.push({
key: key,
value: opArray[key]
});
}
array.sort(function(a, b) {
return a.value < b.value;
});
console.log(JSON.stringify(array));
// will print
//[
// {"key":"3","value":3},
// {"key":"0","value":2},
// {"key":"2","value":1},
// {"key":"1","value":0}
//]
// So now you can iterate over the array in the correct order
// and access the key and value properties
Why not use an ordinary array:
var numOperators = ["-", "*", "/", "+"];
The access is via
numOperators.forEach(function (operator) {
// do something
});
possible.
Example
8*4, result/8, 3-result, result+2
var operators = ['*', '/', '-', '+'],
functions = {
'-': function (a, b) { return b - a; }, // spot the changed variables
'*': function (a, b) { return a * b; },
'/': function (a, b) { return a / b; },
'+': function (a, b) { return a + b; }
},
valArray = [8, 4, 8, 3, 2];
document.write(valArray.reduce(function (r, a, i) {
return functions[operators[i]](r, a);
}, valArray.shift()));
Some usage that I find a bit tricky is
2 in [1, 3, 5]
for (var i in [1, 3, 5]) { ... }
str = "hello";
for (var i in str) { ... }
the first one is true (I might have immediately said 2 in [1, 3, 5] is false), and the second line loops through 0, 1, 2 instead of 1, 3, 5, and the last part loops through 0 to 4, instead of 'h', 'e', 'l', 'l', 'o'. Right now I am just using: whenever seeing in for array, or for-in loop for array, immediately think about index instead. Otherwise, is there a way or usage pattern that we can use to avoid this pitfall?
Yes, it's called "do not use in for arrays". It tests if an object has a certain key - so you cannot properly use it on arrays.
You also shouldn't use for(var x in something) unless you want to iterate over the properties of something.
To check if a value is contained in an array use ~arr.indexOf(value) - or arr.IndexOf(value) != -1 if you want it more verbose.
To iterate over the elements in an array use for(var i = 0; i < arr.length; i++)
You can use indexOf method. jsfiddle
for Array
var arr = [1,2,3,4];
arr.indexOf(2); // will return index of item if exist other wise return -1;
for String
var str = 'Hello world';
str.indexOf('e')
console.log(str.indexOf('e'),str.indexOf('n'));
The in keyword is meant to be used with objects, like this (taken from the MDN site)
// Arrays
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees; // returns true
3 in trees; // returns true
6 in trees; // returns false
"bay" in trees; // returns false (you must specify the index number,
// not the value at that index)
"length" in trees; // returns true (length is an Array property)
// Predefined objects
"PI" in Math; // returns true
var myString = new String("coral");
"length" in myString; // returns true
// Custom objects
var mycar = {make: "Honda", model: "Accord", year: 1998};
"make" in mycar; // returns true
"model" in mycar; // returns true
An array can be thought of as an object. The array ['zero', 'one', 'two'] is like the object {0: 'zero', 1: 'one', 2: 'two'}
So if you write for (i in ['zero', 'one', 'two']) javascript will treat it as if you wrote for (i in {0: 'zero', 1: 'one', 2: 'two'}).
You can check if an object has a particular property value like this:
function isIn(val, obj) {
for (var i in obj) if (obj[i] == val) return true;
return false;
}
isIn('car', ['car', 'horse']) // returns true
If you're specifically checking an array and not just an arbitrary object, you can use the indexOf method which returns the index of its argument or -1 of the array does not contain the argument.
function isInArray(val, arr) {return arr.indexOf(val) > -1;}
isIn('car', ['car', 'horse']) // returns true
Is it possible to get the difference of an associative array and a regular array in Javascript?
Ex.
array1 = [5, 1, 3];
array2 = [1 => 15, 2 => 20, 3 => 10, 4 => 5, 5 =>50 ];
The difference would be...
diff = [2 => 20, 4=> 5];
I assume your question just had a small typo in your declaration of array2. This is not a big deal.
Anyway, here is a bit of hack, but it gives you what you want:
array1 = [5, 1, 3];
array2 = {1: 15, 2: 20, 3: 10, 4: 5, 5: 50};
var result = {};
for (var i in array2) {
if (array1.indexOf(+i) < 0) {
result[i] = array2[i];
}
}
alert(JSON.stringify(result));
Working example
My hack is the +i in the indexOf call, because the properties of your "associative array" are strings, but your array1 contains numbers. The unary + produces a number from a string. Kind of hackish but it is idiomatic an accepted JavaScript practice.
ADDENDUM
As RobG points out, indexOf is an ES5 method, so if your JavaScript engine is ES3 or below, you will need to implement indexOf on your own. An example of how to do this is at MDN. Alternatively, you can just do the equivalent of indexOf by searching the array on your own.
First of all, your second array should be an object and isn't valid JavaScript, as the first two commenters said. Here it is in object form:
var object = { "1": 15, "2": 20, "3": 10, "4": 5, "5": 50 };
This function achieves your desired result:
function findDiff(arr, obj)
{
var tempObj = clone(obj);
for (var i = 0; i < arr.length; i++)
{
var propId = arr[i];
if (tempObj[propId] !== undefined)
delete tempObj[propId];
}
return tempObj;
}
This function relies on a function called clone, which makes sure obj is copied to tempObj by value rather than reference. This prevents the passed object from being modified:
function clone(obj){
if(obj == null || typeof(obj) != 'object')
return obj;
var temp = obj.constructor(); // changed
for(var key in obj)
temp[key] = clone(obj[key]);
return temp;
}
Just call it like this:
var array = [5, 1, 3];
var object = { "1": 15, "2": 20, "3": 10, "4": 5, "5": 50 };
var diff = findDiff(array, object);
You will need to explain in detail what you expect the result of the operation to be.
One interpretation is to remove members from one array based on the values in another, so given:
array1 = [5, 1, 3];
array2 = [15, 20, 10, 5, 50 ];
You might have a function:
function diff(arr1, arr2) {
// Copy arrays so don't affect originals
var t1 = arr1.slice();
var t2 = arr2.slice();
// sort t1
t1.sort();
// Remove members of arr2 from highest indexes to lowest
var i = t1.length;
while (i--) {
t2.splice(t1[i], 1);
}
return t2;
}
alert(diff(array1, array2)); // 15, 10, 50
Note that arrays are zero indexed and there is no 5th member in array2, so the removed values are 20 and 5 (i.e. members with index 1 and 3 respectively).
Another option would be, to do a JSON.stringify() on your variable and check the leftmost or rightmost character of the resulting string. If it is { or }, you have an associative array / object, if [ or ], it is an array. Which I imagine might be a bit costly, depending on the size of your array, but it would do the job.
However... since there is no such thing as a free lunch, and all animals come in pairs, this is only a reasonable solution, if you have used your variable consequently according to your original definition.If you defined an object, like so: obj = [1,2,3] and then, at a later point, added a value such as obj['w'] = 'ww', a JSON.stringify(obj) would only produce [1,2,3], but if you addressed obj['w'] again, you would still get ww, meaning that the value is not forgotten, they just don't mix. Internally, I would imagine, that javascript keeps two separate variable tables, one for objects and one for arrays, and whichever has been defined first, gets the nod when printed with stringify.
And to confuse things still more, if you define a variable as object first, but then add an array key, aka
obj = {1: 11, 2: 22, 3: 33};
obj[4] = 44;
the new key is automatically cast as a string, thus yielding a result in stringify of
{"1":11, "2": 22, "3": 33, "4": 44} //sans the \", for better readibility
Confusing as hell, but I guess that's the price you pay for a little anarchy and ease of use.