Newbie here...be nice.
I have an empty object that will get pushed into an array.
listView = {};
I add properties to it.
listView.code = code;
listView.description = description;
I push the results object into an array.
listy.push(listView);
Each time I enter a new selection in step #2 it overwrites the object instead of adding the new object properties to the array. It also increments the index by one, so it just repeats...
[{"code":"I77.812","description":"Thoracoabdominal Aortic Ectasia"}]
[{"code":"I77.811","description":"Abdominal Aortic Ectasia"},{"code":"I77.811","description":"Abdominal Aortic Ectasia"}]
[{"code":"I06.1","description":"Rheumatic aortic insufficiency"},{"code":"I06.1","description":"Rheumatic aortic insufficiency"},{"code":"I06.1","description":"Rheumatic aortic insufficiency"}]
The array should contain three different objects. But instead it has three copies of the newly added one...
How should I be adding the new choice objects so that they don't get overwritten?
You are always adding a reference to the same object, and changing that same object, instead of adding new objects. See this:
var a = [];
var o = {};
for (var i = 0; i < 5; i++) {
o.id = i;
a.push(o);
}
a
// => [{"id":4},{"id":4},{"id":4},{"id":4},{"id":4}]
But
var a = [];
for (var i = 0; i < 5; i++) {
var o = {};
o.id = i;
a.push(o);
}
a
// => [{"id":0},{"id":1},{"id":2},{"id":3},{"id":4}]
The difference is, the second code always makes a new object that is distinct from all other objects already in the array.
As a metaphor, imagine a theatre director in casting. He turns to an actor, says "You... you'll be Romeo.". Then he looks at the same actor, says "You... you'll be Mercutio. Here, Mercutio, take this sword. Romeo... who told you to get a sword?!?" completely failing to realise that, if Romeo and Mercutio are the same person, if one of them picks up a sword, the other does it too.
Seeing as you declared yourself a 'newbie' i figured i'd take a bit more time explaining. When you push an object to an array, you don't copy the object. You just tell the array where to find the object (a reference). If you push the same object 3 times, the array just has 3 indexes at which it finds the same object. There's several ways around this, the easiest being that you declare the variable inside the loop
for (var i=0;i<3;i++){
var listView = {};
listView.id = i;
listy.push(listView);
}
This way listView is a different reference each time. The other way is to create a new object when you push
listy.push({id:listView.id, description:listView.description});
which works because simple variables are 'copied' into the array and not referenced.
your assignment of the properties of an object are simply replacing the existing properties. wh en you push the object in the array by name, you are push a reference to the object and not a value. This is why all the elements in the array are the same. You need to create a new object every time you push. Something like this should work for you.
listy.push({code:code, description:description});
try this :
listy.push({
code:listView.code,
description : listView.description
})
In my code I have used pass by value.
In your code , you are using Objects which are passed by reference .
You are adding same reference again and again so at the end you will get an array having all the values of same object .
To understand more about pass by value and pass by reference you can reffer this link :
Pass Variables by Reference in Javascript
Related
I want to create an array of objects and then build a JSON string. I have the following code. After trying many times, I found the solution. Can anybody explain the difference between create and the new operator? It looks to me create is making a copy of the object.
HmiUserList = [];
var oco = {};
//If I use the following line it gives wrong string
//oco = Object.create({});
for (var i = 0; i < 3; i++) {
//If I use the following gives right string
oco = new Object();
oco.UserName = i.toString();
oco.GroupName = (i + 2).toString();
var count = HmiUserList.push(oco);
console.log(JSON.stringify(HmiUserList));
}
//Wrong string if I use the create or if I don't use the new for oco
[{"UserName":"2","GroupName":"4"},{"UserName":"2","GroupName":"4"},{"UserName":"2","GroupName":"4"}]
//Target string [{"UserName":"0","GroupName":"2"},{"UserName":"1","GroupName":"3"},{"UserName":"2","GroupName":"4"}]
The difference in behaviour is here not related to using Object.create or new. It is caused by where you put the assignment to oco: in the loop or before the loop.
When you do it before the loop, there is only one object, which the loop keeps mutating, iteration after iteration, and it pushes that same object in the output array. Even though the array ends up with 3 object references, all three represent the same object.
When you do it inside the loop, you create a new object in each iteration, so that your property assignments affect that object, and not an object that was already pushed onto the array. Your array ends up with 3 different objects as intended.
I am trying to get into Fuse to create mobile apps and they use JavaScript for their logic. I never used JavaScript before and just recently completed their getting started course. Most of the stuff is pretty easy to understand, but I am having trouble with the way they use variables at one point. It would be nice, if somebody could explain how variables behave in JavaScript.
So the problem I have goes as follows:
for (var i = 0; i < hikes.length; i++){
// A new variable gets the value of the array
var hike = hikes[i];
if (hike.id == id){
// The variable gets a new value
hike.name = "foo";
break;
}
}
So, in my understanding of programming, the array hikes should be unchanged and only the variable hike should have foo as the name value. But in reality, the array now also has the name foo.
I guess the variable works as a pointer to the address of the arrays value, but maybe somebody can help me to better understand that concept.
Yes you're right, objects and arrays are always passed as references:
a = {}; // empty object
b = a; // references same object
b.foo = 'bar';
a.foo; // also 'bar'
You can create a deep copy of the array using JSON.parse(JSON.stringify(hikes)); and then use that copied array for manipulation:
var hikes = [
{
'id': 10
}
];
var id = 10;
var tempHikes = JSON.parse(JSON.stringify(hikes));
for (var i = 0; i < tempHikes.length; i++){
// A new variable gets the value of the array
var hike = tempHikes[i];
if (hike.id == id){
// The variable gets a new value
hike.name = "foo";
console.log('hike is ', hike);
break;
}
}
console.log(hikes);
arrays in javascript are passed by reference, whenever you modify an element in an array that change will occur anywhere you are accessing that array, to avoid such issues you have to use Array.from(arg) which creates a new array of from the arg parameter. This also applies to objects, to avoid such issues with objects, you have to use Object.create(obj) to create a new obj of from obj parameter or you can use let newObj = Object.assign( {} , obj ) , whenever you make any modification to the members of newObj the obj object does not see it, in other words there is no direct linkage between this two object, same thing applies for array
Boolean, null, undefined, String, and Number values are called primitive types.
When you assign something that is not a primitive type, namely arrays, functions and objects you are storing a reference to that.
That means that hikes[i] contains a reference to the object, where reference roughly means a pointer to it's location in memory.
When you assign hike = hikes[i] you are copying over the reference and not the actual object. So in fact hike still points to the same object as hikes[i], so any changes to that object are visible on both occasions.
If you want to copy the underlying object, there are different ways of doing so. One of them is Object.assign:
var hike = Object.assign({}, hikes[i])
This is because of pass by reference. All you need to do is create a new object (string, number ...) that you can work on.
for (var i = 0; i < hikes.length; i++){
var hike = hikes.slice(i,i+1)[0];
if (hike.id == id){
hike.name = "foo";
break;
}
}
slice also create a deep copy. you can use splice or assign or ((key1, key2)=>(key1, key2))(obj) etc.
I have an array of objects (this object also contains an array of it's own) I'm not sure why, but when I push some values onto the member array of one instance of the object in the array of objects it also seems to push onto the other member arrays on all other array of objects. I have provided my code below:
var ImageGroup = {
GroupName:"",
haz:[]
};
var ImageHandler = {
ImageGroupsArray:[],
image_process: function() {
//Calling the function here...
//Pushing the objects
this.ImageGroupsArray.push(Object.create(ImageGroup));
this.ImageGroupsArray.push(Object.create(ImageGroup));
//Assigning some values
this.ImageGroupsArray[0].haz.push("Dog");
this.ImageGroupsArray[1].haz.push("Cat");
//Should output array with only 'Dog' in it
console.log(this.ImageGroupsArray[0].haz);
//Should output array with only 'Cat' in it
console.log(this.ImageGroupsArray[1].haz);
//Instead, both of these output ["Dog","Cat"]
//this.ImageGroupsArray[1].haz and this.ImageGroupsArray[0].haz point to same 'haz' array??
}
}
This doesn't happen when I try to set the GroupName the same way. What am I doing wrong? Thanks for any help in advance!
This code:
var ImageGroup = {
GroupName:"",
haz:[]
};
creates a single object in memory. This code:
this.ImageGroupsArray.push(Object.create(ImageGroup));
this.ImageGroupsArray.push(Object.create(ImageGroup));
creates two new objects (pushing each onto the ImageGroupsArray). Both of these objects have the same ImageGroup object as their prototype. Just knowing how prototypes work will help you a lot on this one. But basically, this code:
this.ImageGroupsArray[0].haz.push("Dog");
this.ImageGroupsArray[1].haz.push("Cat");
looks for the haz property on each of those Object.create()ed objects. When it can't find it, it looks up the prototype chain and finds it on the parent object (which is the same object in both cases). Any modifications made to this one object will, of course, show up in all places where this object is referenced (so in both the objects you pushed to ImageGroupsArray).
The correct approach is to declare ImageGroup as a function that defines its properties:
var ImageGroup = function() {
this.GroupName = '';
this.haz = [];
}
then use the new keyword, instead of Object.create():
this.ImageGroupsArray.push(new ImageGroup());
this.ImageGroupsArray.push(new ImageGroup());
Cheeers.
You are pushing same object ImageGroup in ImageGroupArray and so it is actually making effect in the same object which is defined globally.
Try this instead
function getImageGroup()
{
var imageGroup = new Object();
imageGroup.GroupName = "";
imageGroup.haz = [];
return imageGroup;
}
this.ImageGroupsArray.push(getImageGroup());
this.ImageGroupsArray.push(getImageGroup());
Working Fiddle
It seems you are referencing the same ImageGroup object twice.
The 'groupName' property gets overwritten, but the array can grow endlessly :-)
Try:
var ImageGroup1 = { GroupName:"foo", haz:[]};
var ImageGroup2 = { GroupName:"bar", haz:[]};
So you get two different objects in stead of two references to the same object.
I would like to use $parse in angularJS to dynamically create a scope variable which references an array and then push to that array; but $parse's assign method does not seem to do the trick.
$scope.life.meaning = [];
var the_string = 'life.meaning';
// Get the model
var model = $parse(the_string);
Would like to push '42' into the array. So the following won't work:
// Assigns a value to it
model.assign($scope, 42);
Are there any other ways to do this? Thanks.
Your example is overwriting the assignment for that object on the scope. The $parse can be used to either get or make assignments. What you need to do it get the array and then push the item onto the array that you got. Because JavaScript arrays are pointers in memory, adding to the array that you retrieved will do the trick.
//get the current value as evaluated against the $scope
var ar = $parse('life.meaning')($scope);
//if the value isn't an array then make it one
if (!Array.isArray(ar)) {
ar = [];
$parse('life.meaning').assign($scope, ar);
}
//add an item to the array
ar.push(42);
I'm working with phylogentic trees and I want an object for the tree itself and then an object for each species, 4 species total. I'm trying to have the tree contain the species objects under tree.leaf and then assign an array of attributes to each species but through the tree object, because I'm randomizing the order of the species so I can't depend on species names but I can use leaf placement(Hope that makes sense). I'm having trouble updating the html, a div inside a table though.
Simplified Version:
var tree = new Object();
var speciesA = new Object();
tree.leaf1 = speciesA;
//Not sure if this next line assigns to speciesA or what exactly happens
tree.leaf1.attributes = new Array("Attr1","Attr2",etc);
var count = 1;
for(attr in speciesA.attributes)
{
//There are 4 divs per speices to display attributes
document.getElementById("A"+String(count)).innerhtml = speciesA.attributes[attr];
count++;// used to specify divs ie A1 = attribute1, A2 = attribute2 etc
}
So I guess my main question is will this work/do what I think it does?
If needed I can pastebin my html and full js files.
What you have should work, but it can be written a bit cleaner. I would suggest this:
var tree = {
leaf1: {attributes: ["Attr1", "Attr2"]}
};
var attributes = tree.leaf1.attributes;
for (var i = 0; i < attributes.length; i++) {
document.getElementById("A"+(i+1)).innerHTML = attributes[i];
}
Things I changed:
Used a javascript literal to make the definition a lot more compact
Used {} and [] for defining arrays and objects rather than new Object() and new Array().
Used for (var i = 0; i < xxx.length; i++) syntax to iterate array elements only, not all properties. This is the "safe" way to iterate elements of an array.
Remove the String(count) as it is not needed. Javascript will auto-convert a number to a string when adding to another string.
Cached the value of the attributes array to save having to deep reference it each time.
Removed separate count variable as the for index can be used
To answer one of your other questions, when you do this:
tree.leaf1 = speciesA;
you have assigned a "reference" to speciesA to tree.left1. A reference is like a pointer. It is not a copy. So, the both refer to exactly the same object. Any change you make to speciesA or to tree.leaf1 is make a change to the exact same object.
So, when you then do this:
//Not sure if this next line assigns to speciesA or what exactly happens
tree.leaf1.attributes = new Array("Attr1","Attr2",etc);
you are indeed modifying the speciesA object since speciesA and tree.leaf1 point to the same object.
In javascript, arrays, objects and strings are assigned by reference. That means that when you assign one to a variable, it just points to the original object. A copy is not made. So, change the object via either either one will change the other (since they both point to the same object). Strings are immutable (a string is never actually changed). Things that feel like modifications to a string always just return a new string so this aspect of javascript doesn't affect strings so much. But, it is very important to know that arrays and objects are assigned by reference.