How to copy object to new variable - javascript

Let's say I have class called Person, which have firstName, lastName, and Array of addresses fields.
And in one of my angular components I am getting Person object to do some operation on them. But I would like to create two copies of this obj. To do that I am using Object.assign. But after that when I am manipulating firstName from firstCopyPerson all other objects are changed too.
How can I assign object to new variable without making reference to orginal object, but instead just creating new separate object?
mainPerson: Person;
firstCopyPerson: Person;
secondCopyPerson: Person;
ngOnInit() {
this.mainPerson = someCache.getPerson();
this.firstCopyPerson: Object.assign({}, this.mainPerson);
this.secondCopyPerson: Object.assign({}, this.mainPerson);
}

You can add this re-usable function so you can reuse this function everytime
const copyObject = (obj) => {
let result = {};
Object.entries(obj).forEach(([key, value]) => {
result[key] = value;
});
return result;
};
// Test the function
const obj1 = {name: 'notebook', price: 100};
objCopy = copyObject(obj1);
console.log(objCopy);
You can also use this way
const obj = {name: 'laptop', price: 100};
// This is the implementation line
let objCopy = {...obj};
console.log(objCopy);

whenever you use this code you are assigning a new reference to the object and both variables assigning to the same object so when you edit one of them the other will change too. you can solve this by destructing the object for example you can try this :
let personCopy = {...person}

Object.assign would only make a shallow copy of the object, that means all attributes are assigned to the same data objects.
You may want to read about deep copies which are the safe way to go.
Fenton already posted a brilliant answer to this, take a look at option 4 in his answer:
https://stackoverflow.com/a/28152032/8341158

Related

Problems with JavaScript object destructuring

let pppp = {
name: "duanxiao",
age: 1,
job: {
title: "~~~"
}
};
let ppppCopy = {};
({
name: ppppCopy.name,
age: ppppCopy.age,
job: ppppCopy.job
} = pppp);
pppp.job.title = "Hacker";
console.log(pppp);
console.log(ppppCopy);
The output values ​​are the same.
Why modifying the value of one object, the other object will also be modified?
Whenever I modify the value of one object, the value of the other object is also modified.
Because you pppp and ppppCopy holds the same reference of job property. Changing at one location will impact another. You can achieve your intended outcome with below code using ES6 spread operator,
let pppp = {
name: "duanxiao",
age: 1,
job: {
title: "~~~"
}
};
const ppppCopy = {
...pppp,
job: { ...pppp.job },
};
With this, updating pppp.job.title will not impact ppppCopy.job.title.
You can also use the traditional way like JSON.parse(JSON.stringify(pppp)), but you need to be more cautious while using this approach as it strips down the function property
In JS, and many other languages, you have data types that store by value, like Number and other primitive types. Some data types stored by reference, like Arrays, Object.
By destructing pppp you just passing the reference to the inner job object, and not duplicating it, so technically its the same job object in pppp and ppppCopy.
Here I added a manipulation to a primitive, and you can see that there is a difference.
let pppp = {
name: "duanxiao",
age: 1,
job: {
title: "~~~"
}
};
let ppppCopy = {};
({
name: ppppCopy.name,
age: ppppCopy.age,
job: ppppCopy.job
} = pppp);
pppp.job.title = "Hacker";
pppp.age = 123;
console.log(pppp);
console.log(ppppCopy);
Here is another good answer related
The only way is to use JSON.parse(JSON.stringify(pppp));
name: "duanxiao",
age: 1,
job: {
title: "~~~"
}
};
let ppppCopy = JSON.parse(JSON.stringify(pppp));
pppp.job.title = "Hacker";
console.log(pppp);
console.log(ppppCopy);
Since objects are non-primitive data types javascript makes a reference of the original object when you make a copy using the assignment operator as you have done in your case. In order to avoid shallow copying you can use the spread operator i.e. let copy = { ...original} or you can use the assign method i.e. let copy = Object.assign({}, original) but both of these fail when you have a nested object. In order to deep copy a nested object you need to do it as Edoardo pointed out above but that will fail too when there is a function in your object. Ravindra's method can also be used, but it will be a hassle when you have multiple nested objects.
The best way to do it in my opinion is to use lodash _.cloneDeep() method. You can read more about it here

Is there any key-value structure that is mutable in JavaScript?

const cache = {};
//adding key-value pair
cache.John = {lastName:"foo"}
//assigning John to a new variable
const John = cache.John;
//changing the cache object
cache.John.lastName = "bar";
console.log(John); // { lastName: 'bar'}
above code shows that even though an object value is saved to a variable and then changed, the variable value also changes.
For my code I need to use a better structure, like a Map, however, when Map value is changed, the reference stays set to the old value.
const cache = new Map();
cache.set("John",{lastName:"foo"});
const John = cache.get("John");
cache.set("John",{lastName:"bar"});
console.log(John); // { lastName: 'foo'}
Is there a way to also "update" the reference using Map? are there any other structures I could use that are mutable in JavaScript?
The reason your Map example isn't working is that you are setting it with a new object after retrieving the original. Instead, you can simply mutate the object from another referenced instance and you'll see the changes in all references.
For more complex interactions you can look at Proxy
const cache = new Map();
cache.set("John",{lastName:"foo"});
const John = cache.get("John");
const temp = cache.get("John");
temp.lastName = 'bar';
console.log(John); // { lastName: 'bar'}
Just use ... to clone the content of object, Like:
const cache = {};
//adding key-value pair
cache.John = {lastName:"foo"}
//assigning John to a new variable
const John = {...cache.John};
//changing the cache object
cache.John.lastName = "bar";
console.log(John); // { lastName: 'bar'}

Can you use a variable name to reference an Object without using eval()

So,
I know it is possible to create dynamic keys on an object.
const foo = 'bar';
const obj = {
[foo]: 'this is bar',
};
But is it possible to reference objects by a variable value. For instance if we have 3 objects:
const obj1 = {}
const obj2 = {}
const obj3 = {}
And an array which holds their names
const objects = ['obj1', 'obj2', 'obj3'];
Can we loop through those names and reference their objects. This won't work:
objects.forEach(obj => Object.assign(obj, { extended: true }));
But this will:
objects.forEach(obj => Object.assign(eval(obj), { extended: true }));
But the docs at Mozilla state "Do not ever use eval!"
The answer is that you can't.
In JS, there's no way to access or create variables dynamically, unless you eval (or the very similar Function constructor).
Although the variables created in the global scope with var will become properties of the global object and you can access them on it, that's also not recommended (due to the problems arisig with var, using reserved names, etc.), and works only in the global scope (which is even worse to mess with).
The best thing you can do is to place these variables into an object instead, and that way you can access them dynamically. JS variables aren't good for that.
So if I've understood you correctly, you have a list of objectKeys, and you want to produce an array of those objects. Would this work?
const objNames = ['obj1', 'obj2', 'obj3' ]
let objects = [];
for(let i = 0; i < objNames.length; i++){
let tempObj = {};
tempObj[objNames[i]] = {extended:true}
objects.push(tempObj);
}
If I understood your question correctly, you can do it like that.
const obj1 = {id: 1}
const obj2 = {id: 2}
const obj3 = {id: 3}
var objects = ['obj1', 'obj2', 'obj3'].map(element => [element]);
console.log('objects : ', objects)
no you can't (without eval()) and it's not a good idea because variable name can be modified for example if you use a minifier
I suggest to use an Array
const objects = [{}, {}, {}]
or, if name is important, an Object:
const objects = {
obj1: {},
obj2: {},
obj3: {},
};
so you can easily iterate it

Best way to reference an object property

Let say I have an object with
let product =
{ id:1,
model:2
}
I can reference the id by
const productId = product.id
Or I can also do
const newProductCreated = Object.assign({}, product)
then reference by newProductCreated.id
Which would be the best way for me to reference the id?
If you are working with the Redux/'Flux' methedology, the second method makes sense, it is not how you should be using it.
Assuming you are trying to create a shallow copy of the object, you can simply do this:
const productCopy = { ...product };
// or const productCopy = Object.assign({}, product) if you can't support ES6
From there, you can make respective changes to the properties within productCopy without affecting the original product.
productCopy.model = 3;
// console.log(productCopy) prints out the modified object
// console.log(product) prints out the original object
This will be conform to the immutable update patterns.

Manipulating object in array changes object outside of array?

I don't understand why this behavior is happening. Lets say I define an object and make an array of 3 of this object. If I modify the objects in the array, it affects all instances of the object? Could someone explain why this is? Also, how do I make an array with independent "Copies" of the object to get the desired behavior? Thanks!
example
testObject = {"value1":"a","value2":"b"};
objArray = [];
for(i=0; i < 3; i++){
var newobj = testObject; //make a new testObject
objArray.push(newobj); //push new object to array
}
delete objArray[0].value2 // Desired, delete value 2 ONLY from array object 0
objArray[2].value2 //Undefined? Why is value2 missing from object 2
testObject.value2 //Undefined? Why is value2 missing from original object?
As opposed to primitives (strings, numbers, booleans, symbols null, undefined), objects in javascript are passed by reference. Variables serve as placeholders/pointers to these objects. To create a copy of an object without the risk of mutation you'd use spread (barring compatibility):
const newObject = { ...testObject };
or traditionally, Object.assign(), passing an empty object literal to avoid mutability of the original testObject:
const newObject = Object.assign({}, testObject);
As far as deep cloning, MDN suggests using a combination of JSON.parse() and JSON.stringify(). So for example:
const testObject = { value: "a", other: { value2: b } };
const newObject = JSON.parse(JSON.stringify(testObject));
You are pushing same object's reference again and again in the loop.
for(i=0; i < 3; i++){
var newobj = testObject; //no new object,same object's reference again
objArray.push(newobj); //push new object to array
}
It should be
for(i=0; i < 3; i++){
var newobj = {"value1":"a","value2":"b"}; //make a new testObject
objArray.push(newobj); //push new object to array
}
When creating an Object in JavaScript, you are actually creating a reference to that object. You can store this in a variable and pass it around ... perhaps append it to an array. When you go to do operations on an object reference, it finds the original object that the reference points to and updates it. Thus when you use .push it's not creating a new object but simply pushing the reference to that object. If you update it in one spot it will update it in the other and any others where you have assigned that reference.
Copying an object into a new object is generally called cloning. There are a lot of different ways to clone objects in JavaScript with varying results.
You can use var newobj = { ...testObject } as the other answer suggests. This spread operator essentially copies the properties of testObject and creates a new object (declared with the outer { }). The reference to that new object is then assigned to newobj. You can think of it as doing this:
var newobj = {
value1: testObject.value1,
value2: testObject.value2,
};
However, you should keep in mind that this gives you only one level of cloning. That is to say if your object contains other objects then the reference to that object will be assigned as the property rather than a clone of that object. For example: let's say you had:
var testObject = { obj: { a: "b" } };
var newobj = { ...testObject };
delete testObject.obj.a;
console.log(newobj); // { obj: {} }
In order to solve this, you need to do what is called a deep clone which in JavaScript can be done by recursively cloning object properties that are also objects. There are a bunch of ways to do this including libraries like lodash or home-grown functions. One example on SO: What is the most efficient way to deep clone an object in JavaScript?
Finally, if testObject is supposed to be something like an object template or initial state from which other newobj are derived, it might make more sense to use a function:
function newObjFactory() {
return {
value1: "a",
value2: "b",
};
}
Then you can do var newobj = newObjFactory() and you'll get a new object each time since a new object is created by the function each time it's called and returned.

Categories

Resources