How does {...object, property: value} work with spread syntax? - javascript

When reviewing the ES6 docs, I noted that it is recommended to use the spread syntax over the more verbose Object.assign() method. But, I am a bit confused as to how this is being accomplished.
Is object in this case being broken down to key: value pairs, after which the property on the right of the comma is either added or overwritten, and finally being reassembled?

Is object in this case being broken down to key: value pairs, after which the property on the right of the comma is either added or overwritten, and finally being reassembled?
The key-value pairs of the original object object are actually being used in combination (merging) with the new object which has an extra property var2( they are getting combined to newObject).
You can think of it as object is becoming a subset of newObject in the place where the spread syntax in being used, while properties with same key are being overridden.
Check the below example:
const object = { txt: 'Test' };
const newObject = {...object, var2: 'aa' };
// logs Object {txt: "Test", var2: "aa"}
console.log(newObject);
const object2 = { txt: 'Test' };
const newObject2 = {...object, txt: 'Test2', var2: 'aa' };
// Object {txt: "Test2", var2: "aa"}
console.log(newObject2);
// merging all objects to a new object
var object3 = {first: 'Hello', second: 'World'};
var newObject3 = {...object, anotherVar: 'stack', ...object2, ...object3};
// Object {txt: "Test", anotherVar: "stack", first: "Hello", second: "World"}
console.log(newObject3);

Related

Setting multiple values to a object

I want to set multiple objects as multiple values of another object. So for example
let dataObj ={}
const personData = {name:"sai",age:"20"}
const ids = {id1:"1231455",id2:"425325232"}
dataObj.personData = personData;
dataObj.ids = ids
console.log(dataObj);
'personData' is a object and 'ids' is a object.Im setting these 2 objects as 2 values for 'dataObj'.
I was wondering is there a shorter way to achieve this like:
let dataObj ={}
const personData = {name:"sai",age:"20"}
const ids = {id1:"1231455",id2:"425325232"}
dataObj = personData;
dataObj = ids
console.log(dataObj);
The problem with the alternative I suggested is that it wont set 'key' of 'dataObj' object.It will just set the values of 'personData' and 'ids' to the value of dataObj.Is there any way I can achieve this ? Specifying multiple objects as values to another object with keys?
You can achieve this using the spread syntax:
let dataObj = {};
const personData = {name:"sai",age:"20"}
const ids = {id1:"1231455",id2:"425325232"}
dataObj = { ...dataObj, personData, ids };
console.log(dataObj);
/*
output :
value of dataObj:
{
personData: {
name: "sai",
age" "20"
},
ids: {
id1: "1231455",
id2: "425325232"
}
}
*/
I'll try and explain spread using the above example. First we start with an empty object dataObj: {}. We want to add new properties to that object: personData with the personData object as value and ids with the ids object as value.
ES2015 added a shorthand syntax for setting property definitions in objects. That's what been used here:
dataObj = { personData, ids };
This is simply a cleaner way of writing
dataObj = {
personData: personData,
ids: ids
};
More info on this can be found on MDN.
Because you want to initialise your object before, we can't simply overwrite the object, but we need to add the new properties to the existing object. This can be done using the spread syntax.
const oldObject = { key: 'value', anotherKey: 'another value' };
let newObject = { ...oldObject };
// newObject value: { key: 'value', anotherKey: 'another value' };
The oldObject was 'spreaded' in newObject. Resulting it in having the same key-value pairs as oldObject.
In your code example we assign a new object to dataObj. In that new object we spread all properties which are in the current dataObj: { ...dataObj and then add the new properties: , personData, ids }.
I hope this explanation is clear, feel free to ask for more info. The spread syntax is very powerful and can be used for a lot of things. So be sure to read up on that.
You can merge two objects as below. You can use '...' spread object. the spread object works for objects as well. Clone an object as shown in below code. for more information please visit https://flaviocopes.com/javascript-spread-operator/
let dataObj = {}
const personData = { name: "sai", age: "20" }
const ids = { id1: "1231455", id2: "425325232" }
dataObj = { ...dataObj, personData, ids }
console.log(dataObj);

Vue changes array in data after making a copy of it

So I have this code in vue:
export default {
name: 'Test',
data() {
return {
test1: ['1', '2', '3'],
test2: [{
name: 'Hello'
}, {
name: 'Number two'
}, {
name: 'What ever'
}],
};
},
created() {
const first = [...this.test1];
first.forEach((elm, index) => first[index] = 'New');
console.log('first: ', first);
console.log('test1 in data', this.test1);
const second = [...this.test2];
second.forEach(elm => elm.name = 'New');
console.log('second: ', second);
console.log('test2 in data', this.test2);
},
}
After setting the value of each item of the array 'first' (that should be a copy without reference to the data 'test1' array) each item is equal to 'new'. The value of this.test1 doesn't change.
I did the same with test2. Copied and changed the value of each item to 'New'. But now the value of the data array 'test2' also has 'New' in every item.
I have no clue why this is like that. Any ideas?
Spread syntax creates a shallow copy. If your array has primitive types like numbers or strings, it won't update the original array. That's the case with test1. In the second case, only a new array is created. If you push or pop from the array, original array won't be updated. But, the objects are still pointing to their same place in memory. Updating them will update original array's objects as well.
You can use the spread syntax on the individual object to create a copy of the objects:
const second = this.test2.map(o => ({...o}))
You can also use JSON.parse and JSON.stringify. But, if the objects have any function properties, they'll be removed.
const second = JSON.parse(JSON.stringify(this.test2))
The reason it is like that is because you are having an array of Vue data values. So even though you are cloning the Array, you are also copying over each values 'getters' and 'setters' which have a reference to the original array. In order to remove the getters and setters you should do what d-h-e has suggested.
You could also do this.
const second = this.test2.map(() => { name: 'New' } );
console.log('second: ', second);
console.log('test2 in data', this.test2);
Try it with:
const second = JSON.parse(JSON.stringify(this.test2));
The copy method with spreadoperator or Array.from works only with simple arrays.
For deep copy use the method with JSON.parse and JSON.stringify.

what's difference between Object.assign() and '= assign' to realize shallow copy in array or object?

To realize shallow copy in object in below code, but the different outputs confuse me:
Object.assign:
var obj = {
name: 'wsscat',
age: 0,
add: {
a: 'beijing'
}
}
var obj2 = Object.assign({}, obj);
obj2.age = 18;
obj2.add.a = 'shanghai';
console.log(obj)
console.log(obj2)
output:
{ name: 'wsscat', age: 0, add: { a: 'shanghai' } }
{ name: 'wsscat', age: 18, add: { a: 'shanghai' } }
while use = "assign" to realize shallow copy:
var obj = {
name: 'wsscat',
age: 0,
add: {
a: 'beijing'
}
}
// var obj2 = Object.assign({}, obj);
var obj2 = obj;
obj2.age = 18;
obj2.add.a = 'shanghai';
console.log(obj)
console.log(obj2)
output:
{ name: 'wsscat', age: 18, add: { a: 'shanghai' } }
{ name: 'wsscat', age: 18, add: { a: 'shanghai' } }
I think you meant 'shallow copy', not 'shadow copy', so I will reference to the former in my answer.
By using assignment operator = you just copy reference, so obj2 points to the same object as obj, so changing property is reflected on both.
The way you used Object.assign, creates shallow clone by copying all own properties from source object obj to target object (empty) which is then assigned to variable obj2.
Primitive data types (null, undefined, String, Number, Boolean) are copied by value, so keys name and age on both objects contain different values in memory). Objects types (Object, Arrray, Function) are copied by reference, so object under add property is shared between both obj and obj2. Any change in obj2.add.a will be reflected on obj.add.a.
Take a look at polyfill implementation of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assigne/
EDIT: Another link with polyfill implementation: https://gist.github.com/spiralx/68cf40d7010d829340cb
EDIT2: Clarified difference between copying primitive types and objects.
Well answered question from Smyk but i want to mention a simple thing.
as he mentioned :
first attempt was a shallow copy of an object which is a good way to avoid pointing to the same reference with flat objects (not nested).
second attempt clearly was pointing to a reference (Array and Object are passing the reference of the variable).
More about that :
there is a problem with shallow copy of objects, because nested object/array is a reference again so in order to fix this, you may have heard about deep copy which is a way to allocate a separate memory location/address for the new object.
an example for that :
var student1 ={
name : "Ahmed",
company : "Tech",
address: {
city: "Tokyo"
}
}
var student2 = JSON.parse(JSON.stringify(student1))
student1.address.city = "Erbil";
console.log(student1.address.city);
console.log(student2.address.city);
More information, here's a link
also to go in depth read this

No functions in Object.keys(document) [duplicate]

What's the difference between Object.getOwnPropertyNames and Object.keys in javascript? Also some examples would be appreciated.
There is a little difference. Object.getOwnPropertyNames(a) returns all own properties of the object a. Object.keys(a) returns all enumerable own properties. It means that if you define your object properties without making some of them enumerable: false these two methods will give you the same result.
It's easy to test:
var a = {};
Object.defineProperties(a, {
one: {enumerable: true, value: 1},
two: {enumerable: false, value: 2},
});
Object.keys(a); // ["one"]
Object.getOwnPropertyNames(a); // ["one", "two"]
If you define a property without providing property attributes descriptor (meaning you don't use Object.defineProperties), for example:
a.test = 21;
then such property becomes an enumerable automatically and both methods produce the same array.
Another difference is in case of array Object.getOwnPropertyNames method will return an extra property that is length.
var x = ["a", "b", "c", "d"];
Object.keys(x); //[ '0', '1', '2', '3' ]
Object.getOwnPropertyNames(x); //[ '0', '1', '2', '3', 'length' ]
Literal notation vs constructor when creating object. Here is something that got me.
const cat1 = {
eat() {},
sleep() {},
talk() {}
};
// here the methods will be part of the Cat Prototype
class Cat {
eat() {}
sleep() {}
talk() {}
}
const cat2 = new Cat()
Object.keys(cat1) // ["eat", "sleep", "talk"]
Object.keys(Object.getPrototypeOf(cat2)) // []
Object.getOwnPropertyNames(cat1) // ["eat", "sleep", "talk"]
Object.getOwnPropertyNames(Object.getPrototypeOf(cat2)) // ["eat", "sleep", "talk"]
cat1 // {eat: function, sleep: function, talk: function}
cat2 // Cat {}
// a partial of a function that is used to do some magic redeclaration of props
function foo(Obj) {
var propNames = Object.keys(Obj);
// I was missing this if
// if (propNames.length === 0) {
// propNames = Object.getOwnPropertyNames(Obj);
// }
for (var prop in propNames) {
var propName = propNames[prop];
APIObject[propName] = "reasign/redefine or sth";
}
}
So in my case the foo function didn't work if I gave it objects of the cat2 type.
There are other ways to create objects so there could be other kinks in there as well.
As was already explained, .keys doesn't return non-enumerable properties.
Regarding to examples, one of pitfall cases is an Error object: some of its properties are non-enumerable.
So while console.log(Object.keys(new Error('some msg'))) yields [],
console.log(Object.getOwnPropertyNames(new Error('some msg'))) yields ["stack", "message"]
console.log(Object.keys(new Error('some msg')));
console.log(Object.getOwnPropertyNames(new Error('some msg')));
Another difference is that (at least with nodejs) "getOwnPropertyNames" function does not guarantee keys order, that's why I usually use "keys" function :
Object.keys(o).forEach(function(k) {
if (!o.propertyIsEnumerable(k)) return;
// do something...
});

Combine or merge JSON on node.js without jQuery

I have multiple JSON like those
var object1 = {name: "John"};
var object2 = {location: "San Jose"};
They are not nesting or anything like that. Just basically different fields. I need to combine them into one single JSON in node.js like this:
{name: "John", location: "San Jose"}
I can use jQuery just fine. Here is a working example in the browser:
http://jsfiddle.net/qhoc/agp54/
But if I do this in node.js, I don't want to load jQuery (which is a bit over use, plus node.js' jQuery doesn't work on my Windows machine).
So is there a simple way to do things similar to $.extend() without jQuery?
You should use "Object.assign()"
There's no need to reinvent the wheel for such a simple use case of shallow merging.
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, target object itself is changed
console.log(obj === o1) // true
Even the folks from Node.js say so:
_extend was never intended to be used outside of internal NodeJS modules. The community found and used it anyway.
It is deprecated and should not be used in new code. JavaScript comes with very similar built-in functionality through Object.assign.
Update:
You could use the spread operator
Since version 8.6, it's possible to natively use the spread operator in Node.js. Example below:
let o1 = { a: 1 };
let o2 = { b: 2 };
let obj = { ...o1, ...o2 }; // { a: 1, b: 2 }
Object.assign still works, though.
**PS1**: If you are actually interested in **deep merging** (in which internal object data -- in any depth -- is recursively merged), you can use packages like [deepmerge][4], [assign-deep][5] or [lodash.merge][6], which are pretty small and simple to use.
**PS2**: Keep in mind that **Object.assign doesn't work with 0.X versions of Node.js**. If you are working with one of those versions (_you really shouldn't by now_), you could use `require("util")._extend` as shown in the Node.js link above -- for more details, check [tobymackenzie's answer to this same question](https://stackoverflow.com/a/22286375/36272).
If using Node version >= 4, use Object.assign() (see Ricardo Nolde's answer).
If using Node 0.x, there is the built in util._extend:
var extend = require('util')._extend
var o = extend({}, {name: "John"});
extend(o, {location: "San Jose"});
It doesn't do a deep copy and only allows two arguments at a time, but is built in. I saw this mentioned on a question about cloning objects in node: https://stackoverflow.com/a/15040626.
If you're concerned about using a "private" method, you could always proxy it:
// myutil.js
exports.extend = require('util')._extend;
and replace it with your own implementation if it ever disappears. This is (approximately) their implementation:
exports.extend = function(origin, add) {
if (!add || (typeof add !== 'object' && add !== null)){
return origin;
}
var keys = Object.keys(add);
var i = keys.length;
while(i--){
origin[keys[i]] = add[keys[i]];
}
return origin;
};
Underscore's extend is the easiest and quickest way to achieve this, like James commented.
Here's an example using underscore:
var _ = require('underscore'), // npm install underscore to install
object1 = {name: "John"},
object2 = {location: "San Jose"};
var target = _.extend(object1, object2);
object 1 will get the properties of object2 and be returned and assigned to target.
You could do it like this as well, depending on whether you mind object1 being modified:
var target = {};
_.extend(target, object1, object2);
A normal loop?
function extend(target) {
var sources = [].slice.call(arguments, 1);
sources.forEach(function (source) {
for (var prop in source) {
target[prop] = source[prop];
}
});
return target;
}
var object3 = extend({}, object1, object2);
That's a basic starting point. You may want to add things like a hasOwnProperty check, or add some logic to handle the case where multiple source objects have a property with the same identifier.
Here's a working example.
Side note: what you are referring to as "JSON" are actually normal JavaScript objects. JSON is simply a text format that shares some syntax with JavaScript.
Use merge.
$ npm install merge
Sample code:
var merge = require('merge'), // npm install -g merge
original, cloned;
console.log(
merge({ one: 'hello' }, { two: 'world' })
); // {"one": "hello", "two": "world"}
original = { x: { y: 1 } };
cloned = merge(true, original);
cloned.x.y++;
console.log(original.x.y, cloned.x.y); // 1, 2
I see that this thread is too old, but I put my answer here just in logging purposes.
In one of the comments above you mentioned that you wanted to use
'express' in your project which has 'connect' library in the
dependency list. Actually 'connect.utils' library contains a 'merge'
method that does the trick. So you can use the 3rd party
implementation without adding any new 3rd party libraries.
Here is simple solution, to merge JSON.
I did the following.
Convert each of the JSON to strings using JSON.stringify(object).
Concatenate all the JSON strings using + operator.
Replace the pattern /}{/g with ","
Parse the result string back to JSON object
var object1 = {name: "John"};
var object2 = {location: "San Jose"};
var merged_object = JSON.parse((JSON.stringify(object1) + JSON.stringify(object2)).replace(/}{/g,","))
The resulting merged JSON will be
{name: "John", location: "San Jose"}
There is an easy way of doing it in Node.js
var object1 = {name: "John"};
var object2 = {location: "San Jose"};
To combine/extend this we can use ... operator in ECMA6
var object1 = {name: "John"};
var object2 = {location: "San Jose"};
var result = {
...object1,
...object2
}
console.log(result)
You can also use this lightweight npm package called absorb
It is 27 lines of code, 1kb and uses recursion to perform deep object merges.
var absorb = require('absorb');
var obj1, obj2;
obj1 = { foo: 123, bar: 456 };
obj2 = { bar: 123, key: 'value' }
absorb(obj1, obj2);
console.log(obj1); // Output: { foo: 123, bar: 123, key: 'value' }
You can also use it to make a clone or only transfer values if they don't exist in the source object, how to do this is detailed in the link provided.
It can easy be done using Object.assign() method -
var object1 = {name: "John"};
var object2 = {location: "San Jose"};
var object3 = Object.assign(object1,object2);
console.log(object3);
now object3 is { name: 'John', location: 'San Jose' }
Use spread operator. It is supported in Node since version 8.6
const object1 = {name: "John"};
const object2 = {location: "San Jose"};
const obj = {...object1, ...object2}
console.log(obj)
// {
// "name": "John",
// "location": "San Jose"
// }
If you need special behaviors like nested object extension or array replacement you can use Node.js's extendify.
var extendify = require('extendify');
_.extend = extendify({
inPlace: false,
arrays : 'replace',
isDeep: true
});
obj1 = {
a:{
arr: [1,2]
},
b: 4
};
obj2 = {
a:{
arr: [3]
}
};
res = _.extend(obj1,obj2);
console.log(JSON.stringify(res)); //{'a':{'arr':[3]},'b':4}
Lodash is a another powerful tool-belt option for these sorts of utilities. See: _.merge() (which is recursive)
var object = {
'a': [{ 'b': 2 }, { 'd': 4 }]
};
var other = {
'a': [{ 'c': 3 }, { 'e': 5 }]
};
_.merge(object, other);
// => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
The below code will help you to merge two JSON object which has nested objects.
function mergeJSON(source1,source2){
/*
* Properties from the Souce1 object will be copied to Source2 Object.
* Note: This method will return a new merged object, Source1 and Source2 original values will not be replaced.
* */
var mergedJSON = Object.create(source2);// Copying Source2 to a new Object
for (var attrname in source1) {
if(mergedJSON.hasOwnProperty(attrname)) {
if ( source1[attrname]!=null && source1[attrname].constructor==Object ) {
/*
* Recursive call if the property is an object,
* Iterate the object and set all properties of the inner object.
*/
mergedJSON[attrname] = zrd3.utils.mergeJSON(source1[attrname], mergedJSON[attrname]);
}
} else {//else copy the property from source1
mergedJSON[attrname] = source1[attrname];
}
}
return mergedJSON;
}
You can use Lodash
const _ = require('lodash');
let firstObject = {'email' : 'email#email.com};
let secondObject = { 'name' : { 'first':message.firstName } };
_.merge(firstObject, secondObject)
A better approach from the correct solution here in order to not alter target:
function extend(){
let sources = [].slice.call(arguments, 0), result = {};
sources.forEach(function (source) {
for (let prop in source) {
result[prop] = source[prop];
}
});
return result;
}
You can do it inline, without changing any variables like this:
let obj1 = { name: 'John' };
let obj2 = { surname: 'Smith' };
let obj = Object.assign({}, obj1, obj2); // { name: 'John', surname: 'Smith' }
Let object1 and object2 be two JSON object.
var object1 = [{"name": "John"}];
var object2 = [{"location": "San Jose"}];
object1.push(object2);
This will simply append object2 in object1:
[{"name":"John"},{"location":"San Jose"}]

Categories