Javascript how to properly clone and not modify an object [duplicate] - javascript

This question already has answers here:
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 6 years ago.
I have an object, which i search the keys and modify the values if a key match is found:
var myData = // some http.get which returns a JSON object.
Imagine myData is:
myData : {
"suffix" : "mr",
"fname" : "jullian",
"lname" : "exor",
"dobGmt" : 145754294700000
"addressLine1" : "xxx",
"street" : "xxx",
"rentStartedGmt" : 145754294700000,
"deposit" : "50.00",
"occupation" : "math teacher",
"profession" : {
"careerStartedGmt": 1458755224800000,
"careerEndGmt": 1459854224800000,
}
}
$scope.viewData = function() {
var objClone = _.clone(myData);
objClone = myFactory.ProcessData(objClone);
$scope.view = objClone;
};
$scope.viewProducts = function() {
};
MyFactory:
myModule.factory('myFactory', function() {
return {
ProcessData: function(data) {
var tmp = data;
function findGmt(tmp) {
for (var key in tmp) {
var v = tmp[key];
if (key.indexOf("Gmt") !== -1) {
tmp[key] = tmp[key].format('DD-MM-YY HH:mm');
}
}
}
findGmt(tmp);
return tmp;
}
}
});
User can click on viewData button which calls $scope.viewData which displays the formatted JSON in a modal on the same page.
User then clicks on viewProducts which calls $scope.viewProducts which displays a list of products in a modal on the same page.
However, after clicking viewProducts, if i go back to click viewData again, on debugging, i can see var objClone is already formatted, rather than taking a new clone of _.clone(myData);
Have a missed how to clone/not modifiy original objects?

Usually, clone functions (that can now be replaced by the standard Object.assign) only copy the provided object properties into a new object. They do not recursively clone the values of the object. That means that the content of your profession property, for example, is the same object for the cloned and the original: myData.profession === objClone.profession is true.
What you are looking for is a deep clone function.

You have to use var copiedObj = angular.copy(myObj). It will create copy of the myObj but changing myObj won't change anything in copiedObj.

Related

How to edit a newly added list from JSON?

I need to clone a data from JSON a key of array, and then edit the newly added key.
Here's the full code i used :
var test_arr = [{'name' : 'data','item':'data2'},{'name' : 'helw','item':'data3'}];
var test_arr1 = test_arr[1];
test_arr.push(test_arr1);
index = 2;
var datas = test_arr[index];
datas.name = 'janjan';
console.log(test_arr);
From the code above, i copied test_arr[1] and pushed it into the test_arr, creating a new index,
I successfully added a new index on test_arr (test_arr[2])
but when i edit test_arr[2], it also edits test_arr[1].
[
{ name: 'data', var2: 'data2' },
{ name: 'janjan', var3: 'data3' },
{ name: 'janjan', var3: 'data3' }
]
Is there a way to only edit test_arr[2] only?
Actually you are doing shallow copy of object. That's why it change the value of object which you used to copy.
You need to consider deep copy of JSON object you just need to modify 2nd line
var test_arr = [{'name' : 'data','item':'data2'},{'name' : 'helw','item':'data3'}];
var test_arr1 = JSON.parse(JSON.stringify(test_arr[1]));
test_arr.push(test_arr1);
index = 2;
var datas = test_arr[index];
datas.name = 'janjan';
console.log(test_arr);

My class objects with randomised values get the exact same values

I'm trying to create multiple objects with different randomised values with a structure from a JSON file. For every new object I create I, for some reason, get the same randomised values as the first object.
I'm doing this by creating an object through a class. I put every "Thingy" created in an array for future reference.
The JSON file is populated with attributes that should, everytime a new Thingy is created, each get a random value from 0-10.
Classes is not my strong side, I'm happy to hear better ways to do this (I'm interested in having funtions inside the Thingies later on).
When I reload the page and create a new Thingy, by pressing my button, the values are beautifully randomised.
The problem is that every time I press my button to create more Thingies the name is randomised BUT every value gets the same random values as the previous Thingy.
At first I thought is was a problem with Javascript using a pseudo random number generator but now I believe I have some wrong understanding on a script language level.
Can anyone shine some light on what I'm doing wrong or not understanding?
Please tell me if I should provide any more information, this is my first post!
CODE:
var i;
var names = ["bjorn", "agnetha", "benny", "annifrid"];
var thingies = [];
var jsonFile = {
"bodyA": {
"sub1": {
"subsub1": null,
"subsub2": null,
"subsub3": null
},
"sub2": {
"subsub1": null,
"subsub2": null
},
"sub3": {
"subsub1": {
"subsubsub1": null,
"subsubsub2": null,
"subsubsub3": null
}
}
},
"bodyB": {
"sub1": {
"subsub1": null
}
},
"bodyC": null
}
class Thingy {
constructor (name, attributes) {
this.name = name;
this.attributes = attributes;
}
}
var button = document.createElement("input");
button.type = "button";
button.value = "Create new thingy";
button.onclick = function() {
// I'm using the math.js to get a random value from the names array (Yes, in this debugging instance it could get the same name as other Thingies)
thingy = new Thingy(math.pickRandom(names), getAttributes(jsonFile));
thingies.push(thingy);
console.log(thingy.name + " was created!");
}
document.getElementById("myDiv").appendChild(button);
function getAttributes(json) {
function iterateObject(json) {
Object.keys(json).forEach(function (k) {
if (json[k] !== null && typeof json[k] === 'object') {
iterateObject(json[k]);
return;
}
if (json[k] === null) {
// This is where I get the exact same values everytime I create a new Thingy after the first one
json[k] = math.randomInt(0, 10); // (Also from math.js - very similiar to default Math)
}
});
return json;
}
return iterateObject(json);
}
function logThingies() {
for (i = 0; i < thingies.length; i++) console.log(thingies[i]);
}
<head>
<!-- math.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/6.6.1/math.js"></script>
</head>
<body>
<div id="myDiv">
<button onclick="logThingies()">Show me all!</button>
</div>
</body>
Because you are creating a random value only when json[k] === null, and then assigning that random value to json[k], so json[k] will never be null again.
In JavaScript, all objects are passed/assigned by reference. That means that when you have an object and assign that object to another variable, or pass the object to another function as an argument, the variable/function will receive the same object that you passed, not a copy of it, and modifying the object received will modify the "base" object you passed to it, as is the same object.
Check this test:
var obj = {
test: 1
};
var obj2 = obj;
console.log("obj.test:", obj.test);
console.log("obj2.test:", obj2.test);
obj.test = 2;
console.log("obj.test:", obj.test);
console.log("obj2.test:", obj2.test);
In short, all your Thingies attributes are a reference to the same object placed in the same space of memory.
What you want to actually do is to create a copy of the object, for example, with something like this (I've just modified a bit your code):
function getAttributes(json) {
var ret = {};
function iterateObject(ret, json) {
Object.keys(json).forEach(function (k) {
if (json[k] !== null && typeof json[k] === 'object') {
iterateObject(ret[k] = {}, json[k]);
} else if (json[k] === null) {
// This is where I get the exact same values everytime I create a new Thingy after the first one
ret[k] = math.randomInt(0, 10); // (Also from math.js - very similiar to default Math)
} else {
ret[k] = json[k];
}
});
}
iterateObject(ret, json);
return ret;
}
Note how I create a new object each time that json[k] is an object. This way you will always receive a new clean object without modifying json.

The key value of one object is changing on update of other similar type of object in javascript

I am using two different array object initialPermissionArr[item.id] and newPermissionArr[roleId] in two different functions
function(item){
vm.initialPermissionArr[item.id] = item.permissions.map(function (permission) {
permission.status = true;
return permission;
});
}
staticArr[item.id] = item.permissions.map(function (permission) {
permission.status = true;
return permission;
});
newpermissionArr[item.id] = vm.initialPermissionArr[item.id];
Below function updates the array, if same object is found it updates the status and if new object is found it pushes to newPermissionArr
function onChanageOfPermission(roleId,item) {
var flag = false ;
for (var key in newpermissionArr[roleId]){
if(newPermissionArr[roleId][key].id == item.id){
flag = true;
newPermissionArr[roleId][key].status = item.status;
break;
}
}
if (!flag) {
newPermissionArr[roleId].push(item);
}
}
So when newPermissionArr[roleId][key].status = item.status; is updated it also update the status in the initialPermissionArr[item.id] also.
And initial declaration is
var newPermissionArr = [];
var staticArr = [];
where for eg item is
{
roleId : 1,
permissions : [{"name": "A", "id" : 1},{ "name" : "B", "id" : 2 }]
}
I need initial object Array to remain same and at the end i need to compare the initial array with the modified array and need to find the reference however on updating the status it updates in both array. How to avoid this ?
The arrays reference the same object. To modify just one of them, you should use slice() function for clone the array:
newpermissionArr[item.id] = vm.initialPermissionArr[item.id].slice();
This is happening because of following line of code
newpermissionArr[item.id] = vm.initialPermissionArr[item.id];
Here object is passed by reference, so whenever newpermission is updated intialpermission will also be updated.
To fix this just copy the intialPermissionArr to newPermissionArr.
Unfortunately,plain javascript does not have any function like angular.copy. So you will have to do this in following way-
newrPermissionArr[item.id] = JSON.parse(JSON.stringify(vm.intialPermissionArr[item.id]));
this should fix your problem.
When you assign something to a and b and it is a pointer in memory to object c. Then as soon as you change it to c2 both a and b will get c2 from that point as they were just pointers to same location.

Javascript Key/Value Pairing with Arrays

I am trying to wrap my head around how I might accomplish something like this, structurally:
var keywordDataProducts =
[
{"keyword" : "keyword1", "list" : [ "DP1", "DP2" ] },
{"keyword" : "keyword2", "list" : [ "DP1" ] }
];
But of course, without the values being hard coded. For instance, we currently loop through all the DP values (DP1, DP2, DP3, etc..) - which all have 0-M keywords. I'm trying to create an inverse lookup of that, where you can get all DPs that use a particular keyword. I have code that uses the structure above perfectly, but I just need the data to get populated more dynamically.
Do I initialize the var keywordDataProducts = []; declaration with anything in it, or define the structure of it having a keyword and a list (which is an array)? Or do you leave it as an array with nothing about it, and define that when you're adding items?
I've heard associative arrays can be used for a situation like this, but I'm not quite wrapping my head around that at the moment. I've also seen objects with {} usages, but there is no push there and I need an array of keywords, which also contains arrays of DPs (list). Thoughts?
You would do something like this, but you didn't clearly describe what the input look like and what output you're looking for.
function fn (input) {
var ouput = {};
input.forEach( function (DP) {
for (prop in DP) {
if (DP.hasOwnProperty(prop) {
if (output[prop]) {
output[prop].push(DP);
} else {
output[prop] = [DP];
}
}
}
});
return output;
}
This takes this kind of input
[{"alpha":...}, {"beta":..., "delta":...}, {"alpha":..., "gamma":...}]
and returns
{"alpha":[{"alpha":...}, {"alpha":..., "gamma":...}]}, "beta":{"beta":..., "delta":...}, "delta":{"beta":..., "delta":...}, "gamma":{"alpha":..., "gamma":...}}
I don't know how you want your output so I just made an object with each keyword as its own key for the DP values.
var data = [{dp: "dp1", keys: ["key1", "key2", "key3"]}, {dp: "dp2", keys: ["key1", "key2", "key3"]}, {dp: "dp3", keys: ["key1", "key2", "key3"]},];
function keyWordArray(arr) {
var newObj = {};
arr.forEach((obj) => {
obj.keys.forEach((keyVal) => {
if(newObj.hasOwnProperty(keyVal)){
newObj[keyVal].dp.push(obj.dp);
} else {
newObj[keyVal] = {dp:[obj.dp],};
}
});
});
return newObj;
}
document.getElementById("data").innerHTML = JSON.stringify(keyWordArray(data));
<div id="data">
</div>
You can treat objects as associative arrays, and you don't have to use "push" to add a new element.
// Create your object like this
var keywordDataProducts =
{
"keyword1" : { "list" : [ "DP1", "DP2"] },
"keyword2" : { "list" : [ "DP1" ] }
};
// Treat it like an associative array
var keyword1 = keywordDataProducts["keyword1"];
alert("keyword1 = " + keyword1.list.join(", "));
// Add to it like this
keywordDataProducts["keyword3"] = { "list" : ["DP3", "DP4"] };
// See the new object includes your new keyword
alert(JSON.stringify(keywordDataProducts));
// To iterate the keys of your object, you can do something like this
for(var item in keywordDataProducts)
{
if(keywordDataProducts.hasOwnProperty(item))
{
alert(item);
}
}
You can see the fiddle here;
https://jsfiddle.net/gksjtwr6/2/

Add Jquery object as key to another object

Can I add Jquery Object to the blank object as key. For example:-
var obj = {};//blank object
var myId = $("#myId");//jQuery Object
var myId2 = $("#myId2");//another jQuery Object
obj[myId] = "Trying to add myId as a key";
obj[myId2] = "Trying to add myId2 as a key";
But the output of the obj contains only one key. Is the above thing is possible in JS or not?
Thanks in advance.
You have to use a string as property name (e.g. the id of the jquery object?).
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
If you want to keep the reference to object you can use an array of objects instead of an object:
[
{
"jQueryElement": myId1,
"note": "Trying to add myId as a key"
},
{
"jQueryElement": myId2,
"note": "Trying to add myId2 as a key"
}
]
Then you will be able to do:
function getNoteOfJqueryObj(element) {
element = $(element);
for (var i in array) {
if (array[i].jQueryElement[0] == element[0]) {
return array[i].note;
}
}
return undefined;
}
I guess that this is one of the best ways you can choose.
JSFIDDLE

Categories