I know about the spread operator ... in JavaScript. We can use it on both arrays and objects:
let user = {
name: "aman",
rollno: 11
}
let newobj1 = {...user}
let newobj2 = [...user]
console.log(newobj1)
console.log(newobj2)
Why does newobj2 give an error, TypeError: user is not iterable, but newobj1 works fine?
{...user} just creates a new object with the same properties as user.
[...user], on the other hand, iterates through the object and adds the values it finds into the array it returns. If there is no way to iterate, as there isn't by default on a plain object (as opposed to an array), then this doesn't work and raises the error you've quoted.
As always, there is much more information about this on MDN. Note in particular the following section:
Spread syntax (other than in the case of spread properties) can only
be applied to iterable objects like Array, or with iterating
functions such as map(), reduce(), and assign().
Many objects are not iterable, including Object:
let obj = {'key1': 'value1'};
let array = [...obj]; // TypeError: obj is not iterable
To use spread syntax with these objects, you will need to provide an
iterator function.
And note that the "spread syntax" being referred to here is the "array spread" version. "Object spread" is rather different and explained on this part of the page,
{...user}
means that you create new object and spread all the data from user inside it.
When you suppose to execute
[...user]
you tries to create brand new array and then spread the user object inside.
Related
When I use the spread operator on a NodeList:
[...document.querySelectorAll("div")]
Does it:
Create a new array
Convert document.querySelectorAll("div") into an array
Unpack the values of document.querySelectorAll("div") into the array literal ([])
The NodeList you get back is iterable (in modern environments), so the effect of
[...document.querySelectorAll("div")]
is the same as it would be to spread from a plain array. It's effectively doing the same thing as
Array.from(document.querySelectorAll("div"))
So yes, a new array is created, then the elements of the query are iteratively copied into the new array. No intermediate array need be built from the NodeList because it's already iterable. You do end up with a plain array.
(Any array initializer with spread syntax creates a new array, just like any traditional use of array initializers does; that's the whole point.)
Note that ... is not, strictly speaking, an operator; it's not part of the expression syntax. (Well it is, in that you can think of the array initializer and object initializer syntax to be part of the overall expression syntax, but it's still not an operator.) It's clumsy not to be able to call it an operator however, so I am personally sympathetic.
The spread operator allow you to do different things (CHECK HERE).
In your case you are creating a new array with all the elements from document.querySelectorAll("div")
const arr = [...document.querySelectorAll("div")];
console.log(arr.length);
console.log(typeof(arr));
<div>
<p>first div</p>
<div>
<p>second nested div</p>
</div>
</div>
The spread operator does not convert the data into an array. It is looking at own and enumerable properties of the iterable object you give to it.
Then it copies them where you tell it to :
in an array : [...obj]
in an object : {...obj}
in arguments: func(...obj)
A real great guide here about spread operator : https://dmitripavlutin.com/object-rest-spread-properties-javascript/
const set = new Set();
set.add('a');
set.add('b');
console.log(typeof set);
console.log(...set);
console.log(...['a', 'b']);
I have some inherited typescript code that looks like this (inside of a function):
const Statuses = [...this.Statuses]
this.Statuses refers to an array of status codes (defined as Statuses = status[];) defined inside the same .ts file. Subsequent to the line above, the code operates on Statuses
I'm trying to understand what the [...this.var] syntax does? Why not simply refer to this.Statuses in the first place?
Yes, I'm new to javascript/typescript....
Thanks in advance.
It's "spread notation", it spreads out this.Statuses (which must be some kind of Iterable, such as an array) into discrete elements. In this case, those form a new array (because the ...this.Statuses is within [], which creates an array).
So if this.Statuses is an array, it's functionally identical to const Statuses = this.Statuses.slice(); (or also the following).
If this.Statuses is another kind of Iterable, it's functionally identical to const Statuses = Array.from(this.Statuses);.
It's a aesthetically-pleasing way of copying an array, to avoid mutations to the original. It uses the spread syntax, as mentioned in other answers.
const a = [ 1 ]
const b = a
const c = [...a]
b.push(2)
c.push(3)
console.log(` original: ${a}`)
console.log(`reference: ${b}`)
console.log(` copy: ${c}`)
It's spread syntax. It allows to copy items from one array (or object) to another. In your case it's used to make a shallow copy of this.Statuses to avoid original array mutations. Thanks for this when you for example push new item to Statuses the original array (this.Statuses) will remain unchanged.
I'm solving a problem where the task is to merge multiple objects from the input array and return a single object with all the keys and values merged. In case a key exists in one or more objects in the input array, the most recent value for that key should be stored in the final returned object.
An example -
var a={1:'1',2:'2',3:'3'},
b={3:'4',5:'6',6:'7',7:'8'},
c={5:'9',8:'9',6:'12',23:'35'}
o=[a,b,c];
The returned object should be -
{ '1': '1','2': '2','3': '4','5': '9','6': '12','7': '8','8': '9','23':'35' }
As an example of the duplicate key case, key 3 exists in both objects a and b, so in the final result the value is {3:'4'} because that is the most recent.
I want to use the spread syntax for Objects in this problem, but that solves only a part of it. What I mean is, if I use spread for each object individually, that works, like -
function foo() {
var a={'1':'1','2':'2','3':'3'},
b={'3':'4','5':'6','6':'7','7':'8'},
c={'5':'9','8':'9','6':'12','23':'35'},
arr = [a,b,c];
return {...arr[0], ...arr[1], ...arr[2]}
}
console.log(foo());
For this to work, I need to write out each array element with spread, as in the above snippet - ...arr[0], ...arr[1], ...arr[2]. However, the input array can contain any number of objects, so writing out each element is not feasible.
Normally, using spread on an iterable like an array, allows you to expand the array elements, like so-
var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
console.log(lyrics)
Is it possible to use spread on the input array to collect all the individual objects, on which spread can be applied again, to get the final object?
You could just use Object.assign():
return Object.assign(...arr);
Here's a complete snippet:
function foo() {
var a={'1':'1','2':'2','3':'3'},
b={'3':'4','5':'6','6':'7','7':'8'},
c={'5':'9','8':'9','6':'12','23':'35'},
arr = [a,b,c];
return Object.assign(...arr);
}
console.log(foo());
Note that this implicitly modifies the first object in your array. If you don't want that, pass a new empty object as the first argument to Object.assign():
return Object.assign({}, ...arr);
You can directly use spread syntax on object to merge them.
function foo() {
var a={'1':'1','2':'2','3':'3'},
b={'3':'4','5':'6','6':'7','7':'8'},
c={'5':'9','8':'9','6':'12','23':'35'};
return {...a, ...b, ...c};
}
console.log(foo());
If you have an array, then you can use array#reduce and Object#assign.
var a={'1':'1','2':'2','3':'3'},
b={'3':'4','5':'6','6':'7','7':'8'},
c={'5':'9','8':'9','6':'12','23':'35'},
arr = [a,b,c];
var result = arr.reduce(function(r,o){
return Object.assign(r,o);
},Object.create(null));
console.log(result);
NOTE : The Rest/Spread Properties for ECMAScript proposal (stage 3) adds spread properties to object literals. It copies own enumerable properties from a provided object onto a new object.
Im writing some code atm with ES6 and Babel (es2015 preset) and I cant spread one Object as I am used to. Normally I would take an Object, use a spread and map the inner content like [...someObject].map(dosomestuff). But my one object does not behave as expected and the only difference I found so far are the keys:
let myObject = {
'key': content,
'key2': content,
'key3': content
};
let array = [...myObject];
Since the object gets generated form a file structure, the keys are formed by variables and can include special chars, so i need to set them like object[key] = value. Why cant I use the spread operator on that object (the array is always empty)? And is there a workaround thats as comfortable as the spread-operator (I dont mean making a new Array with Object.keys and use that)
The object spread works on bojects, you can't spread an object into an array.
You should spread it to another object:
let myObject = {
'key': "content1",
'key2': "content2",
'key3': "content3"
};
let myObject2 = {
'key4': 'content4'
}
let newObj= {...myObject, ...myObject2};
console.log(newObj);
This is not part of ES6, but it is on stage 3
Why cant I use the spread operator on that object?
First of all, ... is not an operator!.
You can only use a spread element when that element is iterable. Objects are not iterable.
And is there a workaround thats as comfortable as the spread-operator (I dont mean making a new Array with Object.keys and use that)
Not in ES6.
I am assuming that you want to get the property values from the object.
If you don't wan to write a helper function and don't necessarily want to use a spread element, you can actually make use of Array.from's ability to take a mapping function as second argument:
Array.from(Array.keys(myObject), x => myObject[x]);
If you really don't want to use Object.keys (Object.values doesn't exist in ES6), then you can write yourself your own helper function as a generator:
function* values(obj) {
for (var x in obj) {
// You can use `obj.hasOwnProperty(x)` to guard against inherited properties
// to achieve the same effect as `Object.keys`
yield obj[x];
}
}
let myObject = {
'key': 'content',
'key2': 'content1',
'key3': 'content2'
};
console.log([...values(myObject)]);
// or
console.log(Array.from(values(myObject)));
With Chrome I see that using spread on an object results in an exception. What does work is using Map
In JavaScript, you can have objects, like this:
var a = { foo: 12, bar: 34 };
Or arrays with key (named) indexes, like this:
var b = [];
b['foo'] = 56;
b['bar'] = 78;
They're somewhat similar, but obviously not the same.
Now the strange thing is, JSON.stringify doesn't seem to take the array. No errors or anything, JSON.stringify(b) just results in [].
See this jsfiddle example. Am I doing something wrong, or do I just misunderstand how arrays work?
Javascript doesn't support Associative arrays (Like PHP).
var b = []; Declaring explicitly an array, when you are trying to create an Object.
Arrays in Javascript can only contain the Index approach of Arrays, while Objects are more of
Associative arrays.
If you change var b = []; to var b = {}; it will solve the problem.
var b = {} Declaring explicitly an Object.
Javascript arrays are objects. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Predefined_Core_Objects#Array_Object for details.
Note: if you supply a non-integer value to the array operator in the
code above, a property will be created in the object representing the
array, instead of an array element.
JSON supports only a subset of Javascript. See http://www.json.org/ for details.
JSON is built on two structures:
A collection of name/value pairs. In various languages, this is realized as an object, record, struct, dictionary, hash table, keyed
list, or associative array.
An ordered list of values. In most languages, this is realized as an array, vector, list, or sequence.
A Javascript array that has properties created in the underlying object does not fit into either of these structures because it has both a collection of name/value pairs and an ordered list of values, so there is no simple way to represent such an object directly in JSON.
The JSON.stringify method is defined in the ECMAScript specification. For example, see http://www.ecma-international.org/ecma-262/5.1/#sec-15.12.3.
While there are many details, the bit that is relevant here is how object values are stringified:
If Type(value) is Object, and IsCallable(value) is false
If the [[Class]] internal property of value is "Array" then Return the result of calling the abstract operation JA with argument value.
Else, return the result of calling the abstract operation JO with argument value.
Given your array, despite the addition of parameters to the underlying object, the result is of stringifying the ordered set of array elements, not the underlying object.
There is nothing wrong about adding parameters to an array object, but they are not part of the array and functions or methods that handle arrays might ignore them or deal with them arbitrarily. You have seen that JSON.stringify ignores the additional parameters. Other functions might do otherwise - you will have to find out in each case.
While it is not wrong, it will probably be easier to understand if you do not add properties to array objects. If you want to add properties, start with a non-array object.
Rather than:
var b = [];
b['foo'] = 56;
b['bar'] = 78;
You might use:
var b = {};
b['foo'] = 56;
b['bar'] = 78;
This snap is from IE explorer. See the array is still blank.
Actually the way of inserting the elements to the array is :
1. Use push()
2. insert the elements in the array during declaration
If you want to stringify the array you have to have the data inside the array.
So, now you want to stringify the key value pairs so you have to pass the object as the argument of JSON.stringify() as follows:
var test = {}; // Object
test['a'] = 'test';
test['b'] = []; // Array
test['b'].push('item');
test['b'].push('item2');
test['b'].push('item3');
var json = JSON.stringify(test);
alert(json);
Solution to your problem now:
Note: Console of Google Chrome is giving different result, which is a bug in Google Chrome.