How to properly declare a Javascript array of objects in an object - javascript

I'm trying to create an object that contains an array of objects. I'm using a jquery selector to determine the number of objects I want in the array. I'm having some issue here because when I enter the for loop Firefox says that "this.obj is undefined" I tried both the the this.obj = new Array() syntax as well as the this.obj[ ]; syntax.
function Base() {
this.n = document.querySelectorAll('transform').length /3;
this.obj = new Array(n); //array declaration
for (var i = 0; i < this.n; i++) {
this.obj[i] = new x3dSphere("b"+i);
}
}
I've only seen examples of arrays within objects that are declared like this.obj = [1,2,2,4] or the in the JSON form obj: [1,2,3,4]. I'm sure this is something really easy to do but I can't seem to find an example anywhere.

The following worked for me:
function Base() {
this.n = 5;
this.obj = [];
for (var i = 0; i < this.n; i++) {
this.obj.push(new Test());
}
}
Where Test was:
var Test = function () {};
It seems like the real problem is that it never created this.obj because n was undefined. If you want to declare the array how you did before, try new Array(this.n).
EDIT
Would new Array(this.n) be faster than the this.obj=[] syntax since it's pre-allocated?
Interestingly enough, the answer is no.
Additionally, the accepted answer on this question has a great discussion of lots of other aspects of JavaScript array performance: What is the performance of Objects/Arrays in JavaScript? (specifically for Google V8)
I've updated my answer to use Array.push() because it is apparently much, much faster than doing Array[i] = new Obj() (shout out to Jason, who used it in his original answer).

You can declare an array with
this.obj = [];
And then push objects into the array in a loop
for (var i =0; i < n; i ++) {
this.obj.push(new x3dSphere ());
}

Related

How to create multiple instances of objects in Javascript

I have a constructor object in my code which is:
function Employee(){
this.name = names[Math.floor(Math.random() * names.length)];
this.age=1;
this.level=1;
this.production=400;
this.totalprod=0;
}
So when I create a new Employee I just say:
var employee1 = new Employee();
So then I can manipulate this instance of the object. Now, I want this objects to be created dynamically with the variable names: employee1, employee2, employee3 and so on.. Is there a way to achieve this or is it impossible?
And the other question that I have, say that I want to change the age of all instances at the same time, is there a way to do this? Thanks in advance and sorry if the question in silly, I'm learning!
EDIT: This is not the same as the other question as I'm using a constructor Object, not a literal Object, and, apart from that, I ask another question which is how to change a property of all instances at the same time, thanks!
This isn't really possible without the use of something like eval, which is bad practice.
Instead if you knew how many employees you wanted to make in advance you could do something like this.
Example: https://jsfiddle.net/dbyw7p9x/
function makeEmployees(n) {
var employees = new Array(n)
for (var i = 0; i < n; ++i) {
employees[i] = new Employee()
}
return employees
}
alternatively you could make also make it return an object which interestingly, while not exactly the same as the array, would be accessed in the same way as an array using numbers inside square brackets obj[0], obj[1], obj[2], obj[3] etc.
function makeEmployeesObj(n) {
var employees = {}
for (var i = 0; i < n; ++i) {
employees[i] = new Employee()
}
return employees
}
To change a property for each approach you can do:
// Array
for (var i = 0; i < e1.length; ++i) {
e1[i].age = 2
}
// Object
Object.keys(e2).forEach(function(key) {
e2[key].age = 2
})
Here is one way to do it, using an array, we push to new Employees to it, and return that array:
To add a specific value, in this case age, I recommend passing it in as a parameter to your Employee constructor, you can do this with all of the this parameters if you like:
Notice in the JsBin that all the ages are different, they are actually the value of n:
Working example: JSBin
function Employee(age){
this.name = 'something';
this.age= age;
this.level=1;
this.production=400;
this.totalprod=0;
}
function maker(n) {
var arr = [];
while (n > 0) {
arr.push(new Employee(n));
n--;
}
return arr;
}

Add a key to an object with keys of an existing array with objects

I've got an array of objects array = [object1, object2, ...], each of them has some keys object1 = { key1: 'value1', ... }. I want to add a key this way:
$rootScope.array[i].newKey = 'someValue'
But angular tells me that $rootScope.array[i] is undefined.
What I've noticed from console is that the objects get the new key but the console still says the same.
You should use less than and not less or equal than comparator.
$scope.init = function () {
for (i = 0; i < /* not <= */ $rootScope.meatTypes.length; i++) {
console.log("I am showing the meatypes:");
console.log($rootScope.meatTypes);
$rootScope.meatTypes[i].counter = '0';
counterOperations.setCounters(i, 0);
}
$rootScope.total = 0;
counterOperations.setTopCounter(0);
};
because when i equals $rootScope.meatTypes.length then $rootScope.meatTypes[i] is undefined.
You are trying to access a member of the array that does not exist.
You need to create a new object and push it onto the array:
$rootScope.array.push({'key1': 'someValue'});
You did not mention lodash, but when I see someone encounter an issue like this, I want to offer the recommendation of using lodash (or underscore.js).
With lodash, you would do something like so, using _.set, which defensively protects against your described issue by automatically adding the necessary elements in the path:
_.set($rootScope, ['array', i, 'newKey'], 'someValue');
This library, properly utilized, solves many issues that you can have with setting and getting variables, ase well as other super useful tools. It has been a major life-saver (and time-saver) for us on our projects.
Like this you can add
$rootScope.array[i] = {}; // first we should create an object on that array location
$rootScope.array[i]['newKey'] = 'someValue'; // then only we can add values
EDIT:
$scope.init = function () {
for (i = 0; i <= $rootScope.meatTypes.length; i++) {
console.log("I am showing the meatypes:");
console.log($rootScope.meatTypes);
**// This is needed**
$rootScope.meatTypes[i]={};// here we should tell that metaType[newItem] is an object other wise it treat it as undefined
$rootScope.meatTypes[i].counter = '0';
counterOperations.setCounters(i, 0);
}
$rootScope.total = 0;
counterOperations.setTopCounter(0);
};

Object.create to create an object with costructor

I am learning Javascript and I am a C++ programmer. I have tried creating an object with a constructor with object.create and here is the result:
var PlayGround ={
initGrid : function(N) {
this.N=N;
this.grid = new Array(N);
for (var i = 0; i < N; i++) {
this.grid[i] = new Array(N);
for (var j = 0; j < N; j++) {
this.grid[i][j] = false;
}
}
return true;
}
};
var PlayGround_property = {
N:{
value: 100,
writable:true
},
grid:{
value:null,
writable:true
}
}
var board= Object.create(PlayGround, PlayGround_property);
It works as I want: the object board contains the object grid, and now I can use the set and get keyword to define the behaviour of the = and () operator.
Anyway I have read around the web that the
this
keyword in Javascript is not safe and I want to be sure that it is referring always to the board object and not to the global window object. Is there a way or I am overthinking?
Other question, are there other ways to write object with a constructor (and maybe other members) in Javascript?
I want to be sure that [this] is referring always to the board object
A function's this is set either by how you call the function, or bind. So just make sure you call methods the right way. If you always call functions as methods of board, then this within the methods will always reference board.
If you are only going to have one instance of board, there doesn't seem much point in using a constructor. If you have multiple instances of board, then you want this to reference the particular instance that called the method so you don't want to fix this using bind.
Crockford just doesn't like the use of new, so encouraged Object.create, it fits his idea of how inheritance should work.
Your pattern could be rewritten to use a constructor something like:
function PlayGround (N) {
this.N = N;
this.grid = []; // Use array literal, it's less to type
for (var i = 0; i < N; i++) {
this.grid[i] = [];
for (var j = 0; j < N; j++) {
this.grid[i][j] = false; // Not sure why you bother with this
}
}
}
var board = new Playground(100);
I'm not exactly sure what you're doing, but that should be close. Note that javascipt is loosely typed, so only initialise variables and properties if you have something useful to assign. Variables are created with a value of undefined, Array properties are only created if you actually assign something to them, creating an array with length N does not create any indexes, e.g.
var arr = new Array(10);
console.log(arr.length); // 10
console.log(arr.hasOwnProperty(0)); // false

Why for-in iterates through array methods also?

on a page I use similar syntax to google analitycs code, to pass parameters to another script. I iterate through the array and try to construct query part of URL according to the parameters in the included script.
The problem is the following iterates through the javascript array objects methods also and mess up resulting queryString.
index.html:
<script>
var params = [];
params['first'] = '1';
params['second'] = '2';
(function() {
var vs = document.createElement('script');
vs.async = true; vs.type = 'text/javascript';
vs.src = 'http://my.domain.com/js/includedScript.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(vs, s);
})();
</script>
includedScript.js:
function(paramsArray) {
tmpArray = [];
for(i in paramsArray) {
tmpArray.push(i + '=' + escape(paramsArray[i]));
}
var queryString = tmpArray.join('&');
}(params);
I got (shortened):
queryString == 'first=1&second=2&push&$family=function%20%28%29%20%7B%0A%20%20%20%20return%20lower%3B%0A%7D&$constructor=function%20Array%28%29%20%7B%0A%20%20%20%20%5Bnative%20code%5D%0A%7D&pop=function%20pop%28%29%20%7B%0A%20%20%20%20%5Bnative%20code%5D%0A%7D&push=function%20push%28%29%20%7B%0A%'
I expect:
queryString == 'first=1&second=2'
It's strange, that on my localhost blank page it's working well. Could some other javascript on the index.html page collide with my code? How can I fix the collision by changing only my code (preferably only the includedScript.js file)?
Thanks in advance.
One small change:
var params = {}; // notice the difference here!
params['first'] = '1';
params['second'] = '2';
and another one here
for(i in paramsArray) {
if(paramsArray.hasOwnProperty(i)){
tmpArray.push(i + '=' + escape(paramsArray[i]));
}
}
JavaScript's Array should only be used with numeric indexes. For associative arrays use Object instead. Also to make sure that the properties you get were added by you and do not belong to the a prototype use hasOwnProperty.
See the documentation on mozilla.org for more info.
An array is an ordered set of values
associated with a single variable
name. Note that you shouldn't use it
as an associative array, use Object
instead.
Also you could read this article:
http://andrewdupont.net/2006/05/18/javascript-associative-arrays-considered-harmful/
Note: The for ... in JavaScript construct has no connection with Array. Its purpose is to iterate over Object properties, not Array elements. In JavaScript properties also include methods.
Fun with JavaScript.
Try the following code and tell me if you're surprised:
var x = [];
x['one'] = 1;
x['one'] = 2;
alert(x.length);
then try this one:
Object.prototype.myProp = 123;
var x = {one: 1, two: 2}
for(var prop in x){
alert(x[prop]);
}
You want to do this
for (i in paramsArray) {
if (paramsArray.hasOwnProperty(i)) {
....
}
}
You should not use for..in to iterate through array elements. Things can be prototyped onto an object automatically to make use of DOM methods or programatically by other javascript on a page (like prototyping).
You should instead get a count of array elements using .length and a normal for loop.
var l = paramsArray.length;
for (var c = 0; c < l; c++) {
// do stuff
}
duncan's answer is correct, and that is because the for-in loop treats the paramsArray as an object, not an array.
And as Alin mentioned, use for (var i = 0; i < paramsArray.length; i++) for arrays.
This is usually an issue when looping using for...in, which is never really recommended.
A way around it is to use the .hasOwnProperty() method to ensure you are getting your own variables:
for (var k in foo) {
if (foo.hasOwnProperty(k)) {
// do something with foo[k]
}
}
Or as others have suggested, use a regular for loop:
for(var k = foo.lenght-1; k >= 0; --k) {
// do something with foo[k]
}
Which is more efficient (especially when reversed)
I love Array.forEach(). It takes a function as an argument. That function receives the element, index, and original list as arguments.
["a","b","c"].forEach(function(item, index, list)
{
console.log("the item at index " + index + " is " + item);
});
To make it compatible with older versions of IE, see here.

Three map implementations in javascript. Which one is better?

I wrote a simple map implementation for some task. Then, out of curiosity, I wrote two more. I like map1 but the code is kinda hard to read. If somebody is interested, I'd appreciate a simple code review.
Which one is better? Do you know some other way to implement this in javascript?
var map = function(arr, func) {
var newarr = [];
for (var i = 0; i < arr.length; i++) {
newarr[i] = func(arr[i]);
}
return newarr;
};
var map1 = function(arr, func) {
if (arr.length === 0) return [];
return [func(arr[0])].concat(funcmap(arr.slice(1), func));
};
var map2 = function(arr, func) {
var iter = function(result, i) {
if (i === arr.length) return result;
result.push(func(arr[i]));
return iter(result, i+1);
};
return iter([], 0);
};
Thanks!
EDIT
I am thinking about such function in general.
For example, right now I am going to use it to iterate like this:
map(['class1', 'class2', 'class3'], function(cls) {
el.removeClass(cls);
});
or
ids = map(elements, extract_id);
/* elements is a collection of html elements,
extract_id is a func that extracts id from innerHTML */
What about the map implementation used natively on Firefox and SpiderMonkey, I think it's very straight forward:
if (!Array.prototype.map) {
Array.prototype.map = function(fun /*, thisp*/) {
var len = this.length >>> 0; // make sure length is a positive number
if (typeof fun != "function") // make sure the first argument is a function
throw new TypeError();
var res = new Array(len); // initialize the resulting array
var thisp = arguments[1]; // an optional 'context' argument
for (var i = 0; i < len; i++) {
if (i in this)
res[i] = fun.call(thisp, this[i], i, this); // fill the resulting array
}
return res;
};
}
If you don't want to extend the Array.prototype, declare it as a normal function expression.
As a reference, map is implemented as following in jQuery
map: function( elems, callback ) {
var ret = [];
// Go through the array, translating each of the items to their
// new value (or values).
for ( var i = 0, length = elems.length; i < length; i++ ) {
var value = callback( elems[ i ], i );
if ( value != null )
ret[ ret.length ] = value;
}
return ret.concat.apply( [], ret );
}
which seems most similar to your first implementation. I'd say the first one is preferred as it is the simplest to read and understand. But if performance is your concern, profile them.
I think that depends on what you want map to do when func might change the array. I would tend to err on the side of simplicity and sample length once.
You can always specify the output size as in
var map = function(arr, func) {
var n = arr.length & 0x7fffffff; // Make sure n is a non-neg integer
var newarr = new Array(n); // Preallocate array size
var USELESS = {};
for (var i = 0; i < n; ++i) {
newarr[i] = func.call(USELESS, arr[i]);
}
return newarr;
};
I used the func.call() form instead of just func(...) instead since I dislike calling user supplied code without specifying what 'this' is, but YMMV.
This first one is most appropriate. Recursing one level for every array item may make sense in a functional language, but in a procedural language without tail-call optimisation it's insane.
However, there is already a map function on Array: it is defined by ECMA-262 Fifth Edition and, as a built-in function, is going to be the optimal choice. Use that:
alert([1,2,3].map(function(n) { return n+3; })); // 4,5,6
The only problem is that Fifth Edition isn't supported by all current browsers: in particular, the Array extensions are not present in IE. But you can fix that with a little remedial work on the Array prototype:
if (!Array.prototype.map) {
Array.prototype.map= function(fn, that) {
var result= new Array(this.length);
for (var i= 0; i<this.length; i++)
if (i in this)
result[i]= fn.call(that, this[i], i, this);
return result;
};
}
This version, as per the ECMA standard, allows an optional object to be passed in to bind to this in the function call, and skips over any missing values (it's legal in JavaScript to have a list of length 3 where there is no second item).
There's something wrong in second method. 'funcmap' shouldn't be changed to 'map1'?
If so - this method loses, as concat() method is expensive - creates new array from given ones, so has to allocate extra memory and execute in O(array1.length + array2.length).
I like your first implementation best - it's definitely easiest to understand and seems quick in execution to me. No extra declaration (like in third way), extra function calls - just one for loop and array.length assignments.
I'd say the first one wins on simplicity (and immediate understandability); performance will be highly dependent on what the engine at hand optimizes, so you'd have to profile in the engines you want to support.

Categories

Resources