Shifting the new variable also shifts the old original one - javascript

How come with this
var prodataTemp = [];
prodataTemp = prodata;
prodataTemp.shift();
both variable prodatTemp and prodata are shifted? I can see it in the console.

Assigning a JavaScript object to another variable, will not copy the contents, but it make the left hand side variable, a reference to the right hand side expression. So,
var prodataTemp = [];
made prodataTemp refer an empty array and then
prodataTemp = prodata;
makes prodataTemp refer the same array object prodata was pointing to. (So, the old empty array is no more referenced by prodataTemp).
To actually make a copy**, use Array.prototype.slice, like this
prodataTemp = prodata.slice();
Now, prodataTemp refers to the copy of the array prodata, so that shifting one will not affect the other.
** - The copy made is just a shallow copy. So, if you have an array of arrays, then a new array will be created with all the references to the elements of the old array. So, mutating one array element will have its impact in the other as well.

Related

JavaScript scoping (copying one array to another)

In this code... mapObj.fetchTimeObjs should NOT change right?!?!
Somehow mapObj.fetchTimeObjs gets changed when this function is run:
function clockErasePast(){
var now = new Date().getTime();
var tmpFetchTimeObjs = [];
for(var i =0; i<mapObj.fetchTimeObjs.length; i++){
tmpFetchTimeObjs.push(mapObj.fetchTimeObjs[i]);
if(mapObj.fetchTimeObjs[i].start < now){tmpFetchTimeObjs[i].start = now;}
}
return tmpFetchTimeObjs;
}
tmpFetchTimeObjs[i]
will contain only reference to the mapObj.fetchTimeObjs[i].
If you will change tmpFetchTimeObjs[i], the mapObj.fetchTimeObjs[i] will be changed, because you will have only one object which has two references. And if it will changed from one reference, it will be changed for the second reference too.
Let's consider an object which has two references. Here I change the object from one reference, and get the update for the second reference, because they refer to the same object.
var objA = { name: 'Bob', age: 25};
var objB = objA;
objB.age = 30;
console.log(objA.age);
To get independent object you need to create them. You can use Object.assign() function, which will copy any enumerable properties from into the destination(first parameter) object and returns it.
You can create with
var obj = Object.assign({}, mapObj.fetchTimeObjs[i]);
tmpFetchTimeObjs.push(obj);
The objects you push to the new array, are the same as the original array has, so if you mutate them, that mutation is visible to both arrays. This is what is called doing a shallow copy.
You could make a copy at one deeper level, where you would also create new objects and copy the original object's properties (like start) into those new objects. This you can easily do with Object.assign:
tmpFetchTimeObjs.push(Object.assign({}, mapObj.fetchTimeObjs[i]));
If these objects themselves have nested objects, then the problem you had would occur at deeper levels. If this is an issue, then you should look to deep clone solutions, like provided in this Q&A.
You need to clone the TimeObjs, since variables only keep the reference to the TimeObjs. We don't know the structure of your TimeObjs, so if the TimeObjs doesn't contain other objects, Object.assign() will work, otherwise maybe you need a deep clone method, such as jQuery.extend().

Weird issue with slice() when copying an array

I have an "empty" array that will be populated with data later in the code. But before it reaches that stage there's a section where the default content is copied to a temporary array, so the original can be changed and later receive the relevant data that was stored in the copy.
The problem is when I use slice and delete the section in the original array, the temporary one is affected as well.
var array1 = [["text", [[[1,2],[3,4],[5,6]]], 0]];
var array2 = array1[0].slice(0);
//alert(array2[1][0]) // Output: 1,2,3,4,5,6
array1[0][1][0] = new Array();
//alert(array2[1][0]) // Output:
http://jsfiddle.net/Mbv6j/4/
I can use a workaround to copy each section of the array separately rather than all at once, but I still would like to understand why this is happening.
This is expected behaviour. Have a look at the documentation.
You only get a shallow copy of the original array:
The slice() method returns a shallow copy of a portion of an array into a new array object.
For the arrays, object references are stored, so just the references get copied. For the String you will not observe this behaviour.
For object references (and not the actual object), slice copies object references into the new array. Both the original and new array refer to the same object. If a referenced object changes, the changes are visible to both the new and original arrays.
For strings and numbers (not String and Number objects), slice copies strings and numbers into the new array. Changes to the string or number in one array does not affect the other array.
Taken from here:
For object references (and not the actual object), slice copies object
references into the new array. Both the original and new array refer
to the same object. If a referenced object changes, the changes are
visible to both the new and original arrays.
My guess is that since your array contains arrays of arrays, they are probably being represented as object references; thus, slice is copying the references, not the objects. It only does a shallow copy, not a deep copy. If the items in your array weren't objects, you wouldn't encounter this problem.

Javascript Object inside of Object

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.

Javascript array index

Sorry for asking a noob question but if I have an array:
MyArray["2cd"]="blah1";
MyArray["3cx"]="blah3";
MyArray["8cz"]="blah2";
And a string myStr="2cd";
And then I use MyArray[myStr] to get the value of blah, how can I get the number I am accessing in the object/array or 0 in this case?
If I may read between the lines, it sounds like you're thinking that the code you posted:
MyArray["2cd"] = "blah1";
MyArray["3cx"] = "blah3";
MyArray["8cz"] = "blah2";
will automatically become the equivalent of:
MyArray[0] = MyArray["2cd"] = "blah1";
MyArray[1] = MyArray["3cx"] = "blah3";
MyArray[2] = MyArray["8cz"] = "blah2";
and therefore you can get the string "blah1" either of these two ways:
var foo = MyArray[0]; // sets foo to "blah1"
var bar = MyArray["2cd"] // also sets bar to "blah1"
But that's not how JavaScript works.
You certainly can set things up so you can use my MyArray[0] and MyArray["2cd"] to fetch the same value, but you have to do it explicitly as in my example.
One thing you didn't mention is how you declared MyArray itself. Is it an Array or an Object? That is, before the code you posted, did you create MyArray with:
var MyArray = {}; // or equivalently, var Array = new Object;
or:
var MyArray = []; // or equivalently, var Array = new Array;
The first example creates an Object, the second an Array.
What is a bit confusing is that JavaScript has both of these two types, which in many cases you can use somewhat interchangeably. But it's customary to use an Object when you are using arbitrary (generally but not necessarily non-numeric) keys into the object, as in your example. Conversely, it's customary to use an Array when you are primarily using strictly numeric indexes.
In fact, JavaScript's Array type inherits from the Object type. An Array is simply an Object with some additional behavior:
An Array has additional methods such as .push() which appends an item to the array.
An Array has a .length property which is automatically updated when you add elements with .push() or a direct array[123] assignment, or when you remove elements with .pop() or other methods.
What JavaScript doesn't have, as Fabrício pointed out, is an "associative array" that behaves like what you might find in some other languages. It has Objects and it has Arrays (which inherit from Objects), and you have to deal with each of those on their own terms.

javascript array attributes passed by reference

in this example, it seems that we can use a variable (here "second") to fill the array myArray, as if second was a "reference" to myArray : is that really what happens here?
var myArray = [];
var second = myArray;
second.target = … //we fill the "second" variable
second.offsetX = …
second.offsetY = …
var target = myArray.target; //then we retrieve the result from myArray
if (target) {
Thanks
second was a "reference" to myArray : is that really what happens here?
Yes.
Objects—like arrays—in JavaScript are passed and assigned by reference.
From your example, myArray and second both point to the same object in memory.
Yes, this is exactly what happens here. When you (for example) push new elements to second, you can read them later from myArray.
BTW, I sense that you're doing something strange. Why do you set an offsetX on an array?
This is called a shallow copy. You have a reference (var second = ...) to the original array (var myArray = ...), they both are pointing to the same memory in the memory of the JavaScript virtual machine.
This way you can access the array either by second or myArray.
var myArray = [];
This is just an array declaration It is same as var myArray=new Array();
About Array Referencing:
var second = myArray;
We are pointing the variable second to myArray memory location. Here new Object second will be created point to content of myArray. So, if you read content of second. It will read the myArray. But, you edit/update the content of second, content of myArray will be copied into second and it will be modified. As Bakudan said, It is the shallow copy. See the example below,
var myArray=[10,20,30];
var second =myArray; //second will contain 23,45 and 100.
If we update the array second, second=[100,200,300]
Original contents will be cleaned and 100,200,300 will be written.
To append the content to array second without removing the original content, We need to use function push as below:
second.push(100);second.push(200),second.push(300);
Now, content of second will be 10,20,30,100,200,300.
Object Property:
second.target = "testString";
second.offsetX =87;
second.offsetY =56;
This is the creation of object properties. It is same as,
second={"target":"testString","offsetX":87,"offsetY":56};
If you want to access value 87, it can be accessed as second.offsetX or second[offsetX].
More Information about java script Array is available here.

Categories