Is there a "native" Javascript function that modifies an object property - javascript

Is there a native JavaScript function (or library/module for JavaScript) that modifies a key's value and then returns the full object?
Here's what I've done:
function modifyJSON(jsob, key, val) {
var newjsob = JSON.parse(JSON.stringify(jsob));
newjsob[key] = val;
return newjsob;
}
(Of course, if I wanted to modify the original, I could delete var newjsob... and work with jsob directly.)
This works, but if this type of functionality has already been implemented somewhere, I'd prefer to use that than roll my own.

Your title is a little misleading. What you're doing is deep cloning an existing object and then changing a property on the new object.
If shallow cloning is enough, you could use Object.assign:
var update = {};
update[key] = val;
return Object.assign(jsob, update);
If you need deep cloning, I would check out ImmutableJS. When updating any field on an immutable data structure, a clone of the original structure with your updates applied to it is returned. It's pretty much the exact behavior you're written.

Basically, the difficult part is the deep cloning. Lodash can deep clone the object for you.
https://lodash.com/docs/4.16.2#cloneDeep
function modifyJSON(jsob, key, val) {
var newjsob = _.cloneDeep(jsob);
newjsob[key] = val;
return newjsob
}
After you get the new object, you can just modify the object's property as you desire.
However, be aware that deep copying is slow. Only use it if you must.
https://jsfiddle.net/38kscyxa/

Related

Prevent object created by reference to change when the original one is changed

Probably this thing was talked before but I haven't find a solution to solve my problem yet.
So, my issue is this:
I'm saving into a variable the value of an object.
const aux = this.myObj;
the Object (this.myObj) is changed by some operations after that but I want to save its initial value and be able to re-assing it to it.
Something like:
this.myObj = aux
but as expected, the aux is also modified when the original one is.
Is there a solution to this?
You need to clone the object and save it's data to the other variable.
var clone = Object.assign({}, obj);
In case of nested object, Deep cloning can be achieved using various methods. One of that for a simple structured object with nested key-value pairs.
var clone = JSON.parse(JSON.stringify(obj));
or use library like underscore and lodash.
Object.assign({}, obj) simply copies property values of the given object so it wouldn't a good fix, I would recommend lodash's _.cloneDeep as it does deep cloning for you.
You would just have just to do the following.
const aux = _.cloneDeep(this.myObj)

How do I deep clone an object in React?

let oldMessages = Object.assign({}, this.state.messages);
// this.state.messages[0].id = 718
console.log(oldMessages[0].id);
// Prints 718
oldMessages[0].id = 123;
console.log(this.state.messages[0].id);
// Prints 123
How can I prevent oldMessages to be a reference, I want to change the value of oldMessages without changing the value of state.messages
You need to make a deep copy. Lodash's cloneDeep makes this easy:
import cloneDeep from 'lodash/cloneDeep';
const oldMessages = cloneDeep(this.state.messages);
oldMessages[0].id = 123;
First let's clarify the difference between shallow and deep clone:
A shallow clone is a clone that has its primitive properties cloned but his REFERENCE properties still reference the original.
Allow me to clarify:
let original = {
foo: "brlja",
howBigIsUniverse: Infinity,
mrMethodLookAtMe: () => "they call me mr. Method",
moo: {
moo: "MOO"
}
};
// shallow copy
let shallow = Object.assign({}, original);
console.log(original, shallow); // looks OK
shallow.moo.moo = "NOT MOO";
console.log(original, shallow); // changing the copy changed the original
Notice how changing the shallow copy's not primitive property's inner properties REFLECTED on the original object.
So why would we use shallow copy?
It is definitely FASTER.
It can be done in pure JS via 1 liner.
When would you use shallow copy?
All of your object's properties are primitives
You are making a partial copy where all your copied properties are primitives
You don't care about the fate of the original (is there a reason to copy and not use that one instead?)
Oke, let's get into making a propper (deep) copy. A deep copy should obviously have the original object coped into the clone by value, not references. And this should persist as we drill deeper into the object. So if we got X levels deep nested object inside of the original's property it should still be a copy not a reference to the same thing in memory.
What most people suggest is to abuse the JSON API. They think that turning an object into a string then back into an object via it will make a deep copy. Well, yes and NO. Let's attempt to do just that.
Extend our original example with:
let falseDeep = JSON.parse(JSON.stringify(original));
falseDeep.moo.moo = "HEY I CAN MOO AGAIN";
console.log(original, falseDeep); // moo.moo is decoupled
Seems ok, right? WRONG!
Take a look at what happened to the mrMethodLookAtMe and howBigIsUniverse properties that I sneaked in from the start :)
One gives back null which is definitely not Infinity and the other one is GONE. Well, that is no bueno.
In short: There are problems with "smarter" values like NaN or Infinity that get turned to null by JSON API. There are FURTHER problems if you use:
methods, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays as your original object's properties.
Why? Well this produces some of the nastiest to track bugs out there.. I have nightmares tracking the disappearing methods or type being turned to another (which passed someone's bad input parameter check but then couldn't produce a valid result) before Typescript became a thing.
Time to wrap this up! So what is the correct answer?
You write your own implementation of a deep copy. I like you but please don't do this when we have a deadline to meet.
Use a deep cloning function provided to you by the library or framework you already use in the project.
Lodash's cloneDeep
Many people still use jQuery. So in our example (please put import where it belongs, on top of the file):
import jQ from "jquery";
let trueDeep = jQ.extend(true, original, {});
console.log(original, trueDeep);
This works, it makes a nice deep copy and is a one-liner. But we had to import the entire jQuery. Which is fine if it is already being used in project, but I tend to avoid it since it is over-bloated and has terribly inconsistent naming.
Similarly, AngularJS users can use angular.copy().
But what if my framework/library does not have a similar function?
You can use my personal SUPERSTAR among JS libraries (I am not involved in the project, just a big fan) - Lodash (or _ for friends).
So extend our example with (again, mind the position of import):
import _ from "lodash"; // cool kids know _ is low-dash
var fastAndDeepCopy = _.cloneDeep(objects);
console.log(original, lodashDeep);
It is a simple oneliner, it works, it is fast.
This is pretty much it :)
Now you know the difference between shallow and deep copy in JS. You realize JSON API abuse is just that, abuse and not a true solution. If you are using jQuery or AngularJS already you now know there is a solution already there for you. If not you can write your own or consider using lodash.
The entire example can be found here:
codesandbox - entire example
Try Using
let tempVar = JSON.parse(JSON.stringify(this.state.statename))
What actually you are doing
let oldMessages = Object.assign({}, this.state.messages);
is a shallow copy which is similar to {...this.state.message} with spread operator.
Object has its own reference in memory to destroy it you can use JSON.parse (JSON.stringify(object))
no matter how nested key it has, it will remove the reference of the object and you will get a new object.
This concept is called a deep copy or deep clone.

Why is my array being altered after using slice()?

I'm loading in a variable from my app-config.js file and then copying it with .slice() in an attempt to prevent its state from being mutated. Much to my chagrin, the function I'm using to alter data seems to be failing to respect this attempt at avoiding mutation. mySensitivityVarskeeps changing, although I'm not quite sure how since I'm only directly acting on mySeries. Any ideas as to why this is happening? Here's the code:
var mySeries = mySensitivityVars.slice();
//Dummy Processing Algorithm
function myDummyAlgo(sliderIndex, newValue, range) {
console.log(mySeries[sliderIndex].data)
var modifier = newValue/(range/2)
var newSeries = mySensitivityVars[sliderIndex].data.map(function(num){
return num * modifier
})
console.log(newSeries)
// mySeries[sliderIndex].data = newSeries
// console.log(sensitivityChart.series[0].data)
sensitivityChart.series[sliderIndex].setData(newSeries);
};
Slice can copy the array but any objects that are referenced inside the array are not getting copied (only a reference is copied)
Without seeing the contents of mySensitivityVars it's hard to tell, but my guess is you're mutating a reference to the original object rather than a duplicate object.
Do you have any objects in mySensitivityVars? If so, the corresponding objects in mySeries will be pointing to the original objects in mySensitivityVars rather than standalone duplicates, which is why you're probably seeing the mutation issues.
You should clone the array instead of copying it if you'd like it to be mutated. you can use JSON.parse(JSON.stringify(mySensitivityVars)) which is pretty fast deep cloning technic.
that would ensure new objects are assigned and not copy of the references.
When you do:
mySeries[sliderIndex].data newSeries;
The array mySeries is a copy of mySensitivityVars, but the objects in the array are not copies. Both arrays contain references to the same objects, and modifying the data property affects them both. You need to copy the objects as well:
mySeries = mySentitivyVars.map(o => Object.assign({}, o));
And if the objects contain references to other objects, you may need to do a deep copy. See What is the most efficient way to deep clone an object in JavaScript?

Retrieved typed objects from IndexedDB

What is the most efficient way to store and retrieve typed Javascript objects in IndexedDB?
The problem is that IndexedDB doesn't store prototype information, so you can only store and retrieve plain objects (or arrays or primitives or a few other types). A workaround I came up with is to explicitly assign __proto__ on objects retrieved from the database. For example, to get a Game object I do
game.__proto__ = Game.prototype;
However, __proto__ assignment has the problem that it is A) technically nonstandard, though supported in practice, and B) deoptimizes the code. In fact, Firefox gives an explicit warning
mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create
Obviously, Object.create isn't a possibility here. Are there any better alternatives to __proto__ assignment?
You could consider storing just the backing-data object itself. Game would become just a proxy for a storable object.
function Game(props) {
this.props = props || {};
}
// An example of property decoration
Game.prototype.set x(value) {
this.props.x = value;
};
Game.prototype.get x() {
return this.props.x;
};
// Use this when initializing a game after retrieving game data from indexedDB store.
// e.g. when creating a new game, use var newGame = Game.fromSerializable(props);
Game.fromSerializable = function(props) {
return new Game(props);
};
// When it comes time to persist the game object, expose the serializable props object
// so that the caller can pass it to store.put/store.add
Game.prototype.toSerializable = function() {
return this.props;
};
This might be simpler than bothering to deal with what can pass through the structured clone algorithm used by indexedDB for read/write, or using some strange one-off hack that other people might struggle to understand.

Javascript is changing variable that needs only to be referenced

I'm pulling some data from a reference object changing it around for what I need, but for some reason my code is also changing the object i'm referencing..
var obj = {name:"list of things", list:[{name:"thing", 1},{name:"other thing", 2}]};
function doStuff () {
var ref = obj;
for(var p=0;p<ref.list.length;p++){
ref.list.splice(1,1);
}
return ref;
}
For some reason where I'm using this structure in my code, its changing 'obj' as well as 'ref'. can't seem to figure it out
The obj in your example is not an object, it is a reference to an object that lives somewhere in memory. This is why when you do ref=obj, you get another reference to the same object, so changing the object ref references is the same as changing the object obj references.
What you want to do is clone your object, so you end up with two different objects. There are some good answers regarding cloning on StackOverflow and the whole web for that matter. Feel free to use any of those.
JavaScript assignes by reference. You would need to do a deep copy in order to clone an object.
See: http://webdevwonders.com/deep-copy-javascript-objects/

Categories

Resources