Delete property from shallow copied object - javascript

If I have one object, and shallow copy of it. For example:
var person = {
name: "Foo",
age: 10,
child: {
name: "Matrix"
}
}
var copiedPerson = {...person}
console.log(person, copiedPerson);
If I change person.name = "NewName";, copedPerson will stay intact.
If I change person.age = 8;, copiedPerson will stay intact.
But if I change person.child.name = "Neo";, copiedPerson.name will also through reference to point also to same name "Neo".
Everything about that is clear to me.
But I do not understand, why when I delete person.child;, nothing happens to copiedPerson.child;
In my logic cause both of them changes when I change one of them, now I also expected when one of them is deleted that other one should also be deleted. (Cause they references to same location in memory)
Can someone explain me why this didn't happen and which part I misunderstood?

There is a little gap in the mental model you're having about the references right now. So essentially, you understand that if you change the nested object person.child.name the value in the copiedPerson.child.name would change too.
But at the end of the day, the child property is only containing the reference of the nested object for both person and the copiedPerson objects.
So, when you delete it from the original person object, you're deleting the reference of this nested object from the person's child property but this nested object still remains in the memory
Hence, the reference contained in the copiedPerson.child still remains untouched and can access this object saved in the memory

delete does not destroy the object that is referenced by a variable or property.
delete removes a property from an object - in your case the person object - just like an assignment adds it.

it is cause the different variables specify on the same object. You should clone obj by cloneObj function
function cloneObj(obj) {
var res = {};
for (var i in obj) {
if (typeof obj[i] == "object") {
res[i] = cloneObj(obj[i]);
} else {
res[i] = obj[i];
}
}
return res;
}
var person = {
name: "Foo",
age: 10,
child: {
name: "Matrix"
}
}
var copiedPerson = cloneObj(person);
copiedPerson.child.name = "Neo";
console.log(person);
console.log(copiedPerson);

Related

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.

Modify nested object and then return the modified parent object

I've got this function
function save(value, ...props)
{
var record;
var allRecords = JSON.parse(window.localStorage.getItem("data"));
record = allRecords;
for (var prop of props)
{
record = record[prop];
}
record = value;
window.localStorage.setItem("data", JSON.stringify(allRecords));
}
whose job is to save a bunch of data in the window.localStorage. However, since JavaScript is a 'reference by value' language, modifying record doesn't affect the parent object (allRecords). So, how can I iterate throughout the storage object, modifying one its children, and then save the modified parent object?
You need to modify the parent object:
function save(value, ...props)
{
var record = allRecords = JSON.parse(window.localStorage.getItem("data"));
var parent=record;
for (var prop of props)
{
parent=record;//store before modifying, so keep the parent
record = record[prop];
}
parent[prop]= value;
window.localStorage.setItem("data", JSON.stringify((allRecords));
return parent;
}
However you now that your function goes "deep" into the object ( just saying):
save("hi","a","b","c");//will save like this
a={
b={
c="hi";
}
}
Another possibility would be stop the loop right before the last iteration, then do record[prop]=value.
1.How objects are stored:
Objects are stored by pointer. So in memory it looks like this (pseudocode):
//a memory location holding an object
432:
val1:1
val2:2
If you say the name of the object is a, a is stored like this:
a = location:432
if you copy it, for example with b=a, just the pointer is copied not the object:
a = location:432
b = location:432
If you change sth in the object a, it will in fact change the object at location 432, therefore b is changed to.
Objects in Objects look the same:
//another obj
100:
obj1: location:432 //our pointer to the object
So our for loop (lets stay at the upper save example) will follow pointer to an object, get a property holding a pointer, than follow this pointer to another object ( a-> b -> c). These objects are stored somewhere in the mem. The JSON.stringify function does deep copy. So it follows every pointer and puts every necessary info into one string ( object a b and c).
"Why does your code work and mys not?":
What you do
value=obj[key];
value="new";
What i do:
obj[key]="new";
But thats the same isnt it? NOPE. Lets look at the data in memory:
//you
//before
value:undefined
obj:
key:"old"
//copying
value:"old"
obj:
key:"old"
//changing
value:"new"
obj:
key:"old" //never changed...

Jquery/javascript creating object

So, I want to create an object (semi-automatically) using Jquery.
Instead of posting all of my code here, I'll give an example of what I want to do:
var myobject = {
'name1': {
'coord1':true,
'coord2':false,
'coord3':false,
},
'name2': {
'coord4':true,
'coord5':false,
'coord6':false,
}
}
1) I first want to check if 'nameX' is already in my object, if so, continue to step 2, if not, I want to add the name, and coordX with value true or false.
2) If 'nameX' is in the array, I want to check i 'coordX' is in the array. If so, I need to check if the corresponding value (true or false) is the same, and if not, replace it. If 'CoordX' is not in the object, I want to add it with the corresponding value.
For example:
var mynewname = 'name3';
var mynewcoord = 'coord5';
var mynewvalue = 'true';
var mynewname2 = 'name1';
var mynewcoord2 = 'coord4';
var mynewvalue = 'false';
When checking these values with the object this should give:
var myobject = {
'name1': {'
coord1':true,
'coord2':false,
'coord3':false,
'coord4':false
},
'name2': {
'coord4':true,
'coord5':false,
'coord6':false
},
'name3':{
'coord5':true
}
}
I hope someone can help me with this. Thank you
In order to find out if an object contains a property with a given name, you have a couple of choices.
You can use the in operator with a string property name:
if ("nameX" in myobject) {
// The object referenced by `myobject` (or its prototype)
// has its own property called "nameX"
}
else {
// It doesn't
}
in will check the object and its prototype. That probably doesn't matter for the simple objects you're using.
Or you can use hasOwnProperty, which only checks the object and not its prototype:
if (myobject.hasOwnProperty("nameX")) {
// The object referenced by `myobject` has its own property called "nameX"
}
else {
// It doesn't
}
So for instance, if you want to see if name1 is in myobject and, if not, add name1 referencing a blank object, you'd do this:
if (!("name1" in myobject)) {
myobject.name1 = {}; // No, add it and give it a blank object as its value
}
And similarly for the coordX properties of the objects you're referencing from myobject.nameX.

Getting a reference to an array element

While I realize that an array, as a non-primitive data type, is handled by references in JavaScript, not by value, any particular element of that array could be a primitive data type, and I assume then that it is not assigned by reference.
I'd like to know how to get a reference to an individual element in an array so that I don't have to keep referring to the array and the index number while changing that element?
i.e.
var myElement=someArray[4]
myElement=5
//now someArray[4]=5
Am I misinterpreting various docs that imply but do not explicitly state that this is not the intended behavior?
You can make a copy of an array element, but you can't create a value that serves as an alias for an array property reference. That's also true for object properties; of course, array element references are object property references.
The closest you could get would be to create an object with a setter that used code to update your array. That would look something like:
var someArray = [ ... whatever ... ];
var obj = {
set element5(value) {
someArray[5] = value;
}
};
Then:
obj.element5 = 20;
would update someArray[5]. That is clearly not really an improvement over someArray[5] = 20.
edit — Now, note that if your array element is an object, then making a copy of the element means making a copy of the reference to the object. Thus:
var someArray = [ { foo: "hello world" } ];
var ref = someArray[0];
Then:
ref.foo = "Goodbye, cruel world!";
will update the "foo" property of the object referenced by someArray[0].
You can always pass around a closure to update this:
var myUpdater = function(x) {
someArray[4] = x;
}
myUpdater(5);
If you want read/write capabilities, box it:
var makeBox = function(arr, n) {
return {
read: function() { return arr[n]; },
write: function(x) { arr[n] = x; }
};
}
// and then:
var ptr = makeBox(someArray, 4);
ptr.read(); // original
ptr.write(newValue);
someArray[4]; // newValue

Removing all properties from a object

I have this Javascript object.
req.session
In my code I add properties to this object. These properties can be other objects, arrays, or just plain strings.
req.session.savedDoc = someObject;
req.session.errors = ['someThing', 'anotherThing', 'thirdThing'];
req.session.message = 'someString'
If I later would like to erase all added properties of this object, what is the easiest/best way?
There must be a better way than this?
// Delete all session values
delete req.session.savedDoc;
delete req.session.errors;
delete req.session.message;
#VisioN's answer works if you want to clear that specific reference, but if you actually want to clear an object I found that this works:
for (var variableKey in vartoClear){
if (vartoClear.hasOwnProperty(variableKey)){
delete vartoClear[variableKey];
}
}
There are two possible solutions to the problem:
Assign an empty object
req.session = {};
The garbage collector will clean the memory automatically. This variant is super fast and will work in most cases, however, you need to use it with caution, as it may keep the references to the objects in memory. This caveat is described in the TLDR section below.
Delete properties one-by-one
Object.keys(object).forEach(key => delete object[key]);
This will clean the object by going through every non-prototype property and deleting it. It's safer but slower. You need to decide if it makes sense for you to use it in a particular case.
TLDR
Any solution given above will do the job for the author in the current situation, as well as any other valid solution provided in this question. It mainly depends on the way how the developer wants to manipulate the deprecated data.
Session object may contain data that is linked by different variable, and setting a new empty object to req.session will not break the reference to the old data, so the old data will be available where it is still required. Although the correct way to keep old data is to clone the initial object, real-life scenarios can be different. Let's look at the following example:
req.session.user = { name: "Alexander" }; // we store an object in the session
var session = req.session; // save reference to the session in a variable
console.log( req.session, session ); // {user: Object}, {user: Object}
req.session = {}; // let's replace session with a new object
console.log( req.session, session ); // {}, {user: Object}
We still can fetch old data from session variable but req.session is empty: here setting a new object works as a sort of alternative to deep cloning. The garbage collector will not remove data from the old req.session object as it is still referenced by the session variable.
Deep cleaning of the object with:
Object.keys(object).forEach(key => delete object[key]);
... will explicitly remove all values from the req.session object and, since session variable is linked to the same object, session will become empty as well. Let's see how it works:
req.session.user = { name: "Alexander" }; // we store an object in the session
var session = req.session; // save reference to the session in a variable
console.log( req.session, session ); // {user: Object}, {user: Object}
Object.keys(req.session).forEach(key => delete req.session[key]);
console.log( req.session, session ); // {}, {}
As you can see now, in both cases we get empty objects.
From speed and memory perspectives setting a new empty object will be much faster than cleaning the old object property by property, however memory-wise if the old data is still referenced somewhere, the new object approach won't free up memory that old data is consuming.
It's quite obvious that choosing the approach to take is mostly up to your coding scenario but in most cases req.session = {}; will do the job: it is fast and short. However, if you keep references to the original object in other variables, you may consider using deep implicit object properties deletion.
I can see only one correct solution for removing own properties from object:
for (var x in objectToClean) if (objectToClean.hasOwnProperty(x)) delete objectToClean[x];
If you want to use it more than once, you should create a cleaning function:
function deleteProperties(objectToClean) {
for (var x in objectToClean) if (objectToClean.hasOwnProperty(x)) delete objectToClean[x];
}
For your case the usage would be:
deleteProperties(req.session);
This solution removes properties from the object wherever it's referenced and keeping the old reference.
Example:
Using empty object assignment:
var x = {a: 5};
var y = x;
x = {}; // x will be empty but y is still {a: 5}, also now reference is gone: x !== y
Using cleaning method:
var x = {a: 5};
var y = x;
deleteProperties(x); // x and y are both empty and x === y
If you want to delete all properties without touching methods you can use :
for(var k in req.session) if(!req.session[k].constructor.toString().match(/^function Function\(/)) delete req.session[k];
You can use a map instead if you care about performance like so
const map = new Map()
map.set("first", 1)
map.set("second", 1)
map.clear()
This is a O(1) operation, so even if you have a huge object you do not need to iterate x times to delete the entries.
I've done it like this
var
i,
keys = Object.keys(obj);
for(i = 0; i < keys.length; i++){
delete obj[keys[i]];
}
You could add it to Object (prototype's not ideal here) - will be static.
Object.defineproperties(Object, {
'clear': function(target){
var
i,
keys = Object.keys(target);
for(i = 0; i < keys.length; i++){
delete target[keys[i]];
}
}
});
Then you can clear random objects with
Object.clear(yourObj);
yourObj = {} replaces the reference to a new object, the above removes it's properties - reference is the same.
The naive object = {} method is okay for vanilla Object, but it deletes prototypes of custom objects.
This method produces an empty object that preserves prototypes, using Object.getPrototypeOf() and Object.create():
emptyObj = Object.create(Object.getPrototypeOf(obj), {});
Example:
class Custom extends Object {
custom() {}
}
let custom = new Custom();
custom.foo = "bar";
console.log(custom.constructor.name, custom);
// Custom {"foo": "bar"}
// naive method:
let objVanilla = {}
console.log(objVanilla.constructor.name, objVanilla);
// Object {}
// safe method:
objSafe = Object.create(Object.getPrototypeOf(custom), {});
console.log(objSafe.constructor.name, objSafe);
// Custom {}
This script removes property recursively except for the data reported in vector.
You need the lodash library
-- Function:
function removeKeysExcept(object, keysExcept = [], isFirstLevel = true) {
let arrayKeysExcept = [],
arrayNextKeysExcept = {};
_.forEach(keysExcept, (value, i) => {
let j = value.split('.');
let keyExcept = j[0];
arrayKeysExcept.push(keyExcept);
j.shift();
if (j.length) {
j = j.join('.');
if (!arrayNextKeysExcept[keyExcept]) {
arrayNextKeysExcept[keyExcept] = [];
}
arrayNextKeysExcept[keyExcept].push(j);
}
})
_.forEach(arrayNextKeysExcept, (value, key) => {
removeKeysExcept(object[key], value, false);
});
if (isFirstLevel) {
return;
}
Object.keys(object).forEach(function (key) {
if (arrayKeysExcept.indexOf(key) == -1) {
delete object[key];
}
});
}
Run so:
-- Removes all properties except the first level and reported in the vector:
removeKeysExcept(obj, ['department.id','user.id']);
-- Removes all properties
removeKeysExcept(obj, ['department.id','user.id'], false);
-- Example:
let obj = {
a: {
aa: 1,
ab: {
aba: 21
}
},
b: 10,
c: {
ca: 100,
cb: 200
}
};
removeKeysExcept(obj, ['a.ab.aba','c.ca']);
/*OUTPUT: {
a: {
ab: {
aba: 21
}
},
b: 10,
c: {
ca: 100,
}
};*/
removeKeysExcept(obj, ['a.ab.aba','c.ca'], false); //Remove too firt level
/*OUTPUT: {
a: {
ab: {
aba: 21
}
},
c: {
ca: 100,
}
};*/
removeKeysExcept(obj);
/*OUTPUT: {b:10};*/
removeKeysExcept(obj, [], false); //Remove too firt level
/*OUTPUT: {};*/

Categories

Resources