Javascript Object inside of Object - javascript

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.

Related

Weird Array Objects - JavaScript

Arrays are quite something in JavaScript when compared with other programming languages and it's not without its full set of quirks.
Including this one:
// Making a normal array.
var normalArray = [];
normalArray.length = 0;
normalArray.push(1);
normalArray[1] = 2;
normalArray; // returns [1, 2]
normalArray.length // returns 2
So yes, the above is how we all know to make arrays and fill them with elements, right? (ignore the normalArray.length = 0 part for now)
But why is it that when the same sequence is applied on an object that's not purely an array, it looks a bit different and its length property is off by a bit?
// Making an object that inherits from the array prototype (i.e.: custom array)
var customArray = new (function MyArray() {
this.__proto__ = Object.create(Array.prototype);
return this
});
customArray.length = 0;
customArray.push(1);
customArray[1] = 2;
customArray; // returns [1, 1: 2]
customArray.length // returns 1
Not entirely sure what's going on here but some explanation will be much appreciated.
This may not be the perfect answer, but according to my understanding of Javascript arrays, they are a little bit different than usual objects. (Mainly due to the fact that it maintains a length property, and Objects don't).
So if we take your code for an example:
var normalArray = [];
This is the right way to create an array in Javascript. But what about the below one?
var customArray = new (function MyArray() {
this.__proto__ = Object.create(Array.prototype);
return this
});
Are they same? Let's see..
Array.isArray(normalArray); // true -> [object Array]
Array.isArray(customArray); // false -> [object Object]
So it is clear that although you inherit from the array prototype, it doesn't really create an object with Array type. It just creates a plain JS object, but with the inherited array functions. That's the reason why it updates the length when you set the value with customArray.push(1);.
But since your customArray is only a regular object and for a regular JS object, [] notation is used to set a property, it doesn't update the length (because Objects don't have a length property)
Hope it's clear :)
The array you are trying to create is not a pure array (as you are perhaps aware). Its basically a JavaScript object and is supposed to behave like an object.
While treating an object like an array, its up to you to maintain all it's array like features.
You specifically have to assign a length property to it and you did it correctly.
Next, the push method from Array.prototype is supposed to insert an element to the array and increment the length property (if any), so it did increment 0 to 1. There you go, the length now is 1.
Next you used the literal notation of property assignment to Object, which is similar to something like customArray['someProperty'] = 1.
While using literal notation, no method from Array.Prototype is being invoked and hence the customArray object never knows that it has to behave like an Array and its length property remains unaffected. It simply behaves like an object and you get what you got.
Remember the length is just a property on Array class and this property is appropriately incremented and decremented by every method on Array.
Note: Array like objects are not recommended and its up to you entirely to maintain the index and other Array stuff for such objects.
From what I can see, you have a problem with your function:
return this
This should be
return (this);
Just fixes any potential errors you might have. Another thing is you're not using the var keyword to declare customArray. These errors might be breaking your code.

New objects get overwritten when being added to an array

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

Javascript: Change name of an array into a string

Below, I have an array of arrays of objects. I go through looking for my object, and once I find which array it's in, I want to get at and work with that array's name as a string. My guess, was something like Array.name (as it plays out below), but that doesn't work.
ActiveDocument.gaShapesTab1 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape1"],ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape2"]);
ActiveDocument.gaShapesTab2 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape3"],ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape4"]);
ActiveDocument.gaShapesTab3 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape5"],ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape6"]);
ActiveDocument.gaShapeArrays = new Array(gaShapesTab1, gaShapesTab2, gaShapesTab3);
// go through an array of arrays
for(var x=0; x<gaShapeArrays.length; x++)
{
// and go through the objects of each one
for(var y=0; y<gaShapeArrays[x].length; y++)
{
// if "object" is in the array
if(object == gaShapeArrays[x][y])
{
// get "sidetab" from object's array's name
var sidetab = gaShapeArrays[x].name.replace('gaShapes',''); // assumes that shapearrays will have naming convention gaShapesSidetab
// we found it, we can stop now
break;
}
}
}
I'm working in Hyperion Intelligence, so not all Javascript will apply. For instance I don't have access to window or document.
Each array contains a set of shape objects related to a visual tab. This allows me to show or hide or do more complex operation with what's on each tab simply by calling the array of shapes. But, when working with the shapes, themselves, I need to know which tab they're on. I'm trying to work backwards by finding which array they're in.
You don't want to do that.
If you really need to find a value in several arrays and then pull out an identifier, then you want a dictionary, not named variables:
var dictOfArrays = {
'evens': [0,2,4,6,8,10],
'odds': [1,3,5,7,9]
};
This stores the identifier that you seek as data, so you can store that identifier and use it later to retrieve the value if you want:
var whichArrayKey = findMyValuesKey(value, dictOfArrays);
console.log('Value '+value+' is in array keyed '+whichArrayKey);
var matchingArray = dictOfArrays[whichArrayKey];
var firstValueInMatchingArray = matchingArray[0];
The name of a variable is just something for you, the developer, to use to know which thing is which. It's just a handle for a place in memory where stuff is stored. As such, it doesn't mean anything to the code. If you actually want to use it in the program, then it is data, not code, and should be encoded in a data structure like a dictionary as above. That way you can pass the array or the identifier around as much as you please, and the behaviour of the code doesn't have to be tied to the names you give your variables.
Edit 1:
The newly added code, in dictionary form/object notation:
ActiveDocument.gaShapeArrays = {
'gaShapesTab1' : [
ActiveDocument.Sections["Dashboard"].Shapes["Shape1"],
ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape2"]
],
'gaShapesTab2' : [
ActiveDocument.Sections["Dashboard"].Shapes["Shape3"],
ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape4"]
],
'gaShapesTab3' : [
ActiveDocument.Sections["Dashboard"].Shapes["Shape5"],
ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape6"]
]
}
So each key (e.g. 'gaShapesTab1') is paired with an array value ([...]). This is instead of using new Array() everywhere.
Once you have found the key of the array containing a reference matching your object, you'll have that key as a string (e.g. "gaShapesTab3"). You can't change this string in-place, and I don't think you'd want to. If you could clarify why you need to change the name of the array, perhaps it will be clear how to resolve the problem. For example, do you have other code that needs the array to have a particular name?
Array's name? Arrays do not have names. You only have variable names, variables that store your arrays. If you have a two-dimensional array, you need to grab the "coordinates".
So:
if(object == gaShapeArrays[x][y])
{
// Found the object! It's in [x][y], so in array gaShapeArrays[x] which
// itself is in gaShapeArrays
}
Even though I think #Phil H gave me the answer to my question, as the proper way to do it, I have other reasons to do it the way #ben336 was commenting. It might not be proper, but I'm posting what the solution was in the end. Fortunately, I already had the gaSidetabs array elsewhere in my startup script for another function. I just assigned a string value to the .name property of each array. Would've been nice to know if there was a way to "get at" the symbolic name (or whatever you want to call it) that I called the array, but it sounds like that's just not possible.
ActiveDocument.gaShapesTab1 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape1"],ActiveDocument.Sections["Dashboard"].Shapes["Shape2"]);
ActiveDocument.gaShapesTab2 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape3"],ActiveDocument.Sections["Dashboard"].Shapes["Shape4"]);
ActiveDocument.gaShapesTab3 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape5"],ActiveDocument.Sections["Dashboard"].Shapes["Shape6"]);
ActiveDocument.gaShapeArrays = new Array(gaShapesTab1, gaShapesTab2, gaShapesTab3);
ActiveDocument.gaSidetabs = new Array('Tab1','Tab2','Tab3');
// Assigns a .name javascript property to each array. assumes the order and length of the arrays is the same.
if (gaShapeArrays.length == gaSidetabs.length)
{
for (var x = 0; x < gaShapeArrays.length; x++)
{
gaShapeArrays[x].name = gaSidetabs[x];
}
}
else
{
Console.Writeln('Warning: gaShapeArrays and gaSidetabs are not the same length. Some names will not be assigned.');
}
// go through an array of arrays
for(var x=0; x<gaShapeArrays.length; x++)
{
// and go through the objects of each one
for(var y=0; y<gaShapeArrays[x].length; y++)
{
// if "object" is in the array
if(object == gaShapeArrays[x][y])
{
// get "sidetab" from object's array's name
var sidetab = gaShapeArrays[x].name.replace('gaShapes',''); // assumes that shapearrays will have naming convention gaShapesSidetab
// we found it, we can stop now
break;
}
}
}
Alert(sidetab);
Also glad I could figure out how to retain the format of the code block, here.

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.

Creating multi-dimensional arrays in javascript, error in custom function

I was trying to define an array (including other arrays as values) in a single javascript statement, that I can loop through to validate a form on submission.
The function I wrote to (try to) create inline arrays follows:
function arr(){
var inc;
var tempa = new Array(Math.round(arguments.length/2));
for(inc=0; inc<arguments.length; inc=inc+2) {
tempa[arguments[inc]]=arguments[inc+1];
}
return tempa;
}
This is called three times here to assign an array:
window.validArr = arr(
'f-county',arr('maxlen',10, 'minlen',1),
'f-postcode',arr('maxlen',8, 'minlen',6)
);
However in the javascript debugger the variable is empty, and the arr() function is not returning anything. Does anyone know why my expectations on what this code should do are incorrect?
(I have worked out how to create the array without this function, but I'm curious why this code doesn't work (I thought I understood javascript better than this).)
Well from what your code does, you're not really making arrays. In JavaScript, the thing that makes arrays special is the management of the numerically indexed properties. Otherwise they're just objects, so they can have other properties too, but if you're not using arrays as arrays you might as well just use objects:
function arr(){
var inc;
var tempa = {};
for(inc=0; inc<arguments.length; inc=inc+2) {
tempa[arguments[inc]]=arguments[inc+1];
}
return tempa;
}
What you're seeing from the debugger is the result of it attempting to show you your array as a real array should be shown: that is, its numerically indexed properties. If you call your "arr()" function as is and then look at (from your example) the "f-county" property of the result, you'll see something there.
Also, if you do find yourself wanting a real array, there's absolutely no point in initializing them to a particular size. Just create a new array with []:
var tempa = [];
Your code works. Just inspect your variable, and you will see that the array has the custom keys on it. If not expanded, your debugger shows you just the (numerical) indixed values in short syntax - none for you.
But, you may need to understand the difference between Arrays and Objects. An Object is just key-value-pairs (you could call it a "map"), and its prototype. An Array is a special type of object. It has special prototype methods, a length functionality and a different approach: to store index-value-pairs (even though indexes are still keys). So, you shouldn't use an Array as an associative array.
Therefore, their literal syntax differs:
var array = ["indexed with key 0", "indexed with key 1", ...];
var object = {"custom":"keyed as 'custom'", "another":"string", ...};
// but you still can add keys to array objects:
array.custom = "keyed as 'custom'";

Categories