I have "scene" with graphics "objects"...
Scene.prototype.objects=new Array();
Scene.prototype.add=function(obj){
var last=this.objects.length;
this.objects[last]=obj}
Scene.prototype.remove=function(obj){
this.objects.splice(obj.id,1)}
Scene.prototype.advance=function(){
for (var id in this.objects){
var obj=this.objects[id];
obj.id=id;
obj.advance();
}
}
Scene.prototype.paint=function(context){...}
each time creating and deleting many objects. Array.prototype.splice re-index array right? Does anyone know a better technique (adding and removing on javascript Array)?
In my opinion, is another possibility to do that something like
Scene.prototype.remove=function(obj){
delete this.objects[obj.id]; // don,t care about this.objects.length
delete obj; // not necessary...
}
I have not tried it yet...
I need a good book about JavaScript :)
Your delete method wouldn't work, because objects is an Array, and obj.id is the id of the object reference stored in an element in that Array. splice would be the method to use, but you'll have to know the index of the element in the Array. Maybe you should 'remeber' it this way:
Scene.prototype.add=function(obj){
var last=this.objects.length;
obj.objectsIndex = last;
this.objects[last]=obj
}
After which you can:
Scene.prototype.remove=function(obj){
this.objects.splice(obj.objectsIndex,1)};
//reindex the objects within the objects Array
for (var i=0; i<this.objects.length;i++){
this.objects[i].objectsIndex = i;
}
}
Note: Adding the objects Array to the prototype of your Scene constructor means it will be the same for all instances (static), is that what you want?
It seems you don't actually need an array for your objects. You can use another object has hash table:
(function() {
var i = 0;
Scene.prototype.objects = {};
Scene.prototype.add = function(obj) {
var id = i++;
this.objects[id] = obj;
obj.id = id;
};
Scene.prototype.remove = function(obj){
if(obj.id in this.objects) {
delete this.objects[obj.id];
}
};
Scene.prototype.advance=function(){
for (var id in this.objects){
var obj=this.objects[id];
obj.id=id;
obj.advance();
}
};
Scene.prototype.paint=function(context){...}
}());
I'd rather avoid using new Array() instead use [] (page 114 of book I mentioned in comment - 'JavaScript: The Good Parts' by Douglas Crockford ;)).
What's more, I don't think adding objects array to Scene prototype will work as you expect. You probably want to achieve some Object-Oriented structure, which requires some acrobatic skills in JavaScript, as it uses prototypal inheritance as it is class-free.
Try something like this:
var Scene = function(initial_objects) {
this.objects = initial_objects || [];
};
Scene.prototype.add = function(obj) {
this.objects.push(obj);
};
Scene.prototype.remove_by_index = function(index) {
this.objects.splice(index, 1);
};
Scene.prototype.remove = function(obj) {
var index = this.objects.indexOf(obj);
if (index > -1) {
this.objects.splice(index, 1);
}
};
Read also this: http://javascript.crockford.com/inheritance.html
Related
I got to the point with my project where I decided to simplify some of the js functions where I am looking for a parent in a DOM tree, then drill down to the elements many many times in one function. instead I though I will make instances of a function which will keep some data so then I can refer and operate on objects in easy way. I got it working but as I was going along, I decided to extend functionality and add some extra functions like getElementsByClassNameThenTagName.
I loop through the arrays and if add matching elements to the array.
I have noticed (sadly only now) that I am creating an array with elements rather than HTML collection. As a results, I cannot refer to the objects in my findings by typing buttons['reset'].disabled = false;. I can access my reset button by buttons[3].disabled = false; but this would cause a lot of inconvenience.
I am therefore looking for a way to convert my array with object into a HTML collection.
Please see below my current function:
this.getElementsByClassNameThenTagName = function (elementClass, elementTag) {
if (parentNode == null) {
this.init();
}
var results = [];
var regexStr = elementClass;
var regex = new RegExp(regexStr);
var x = moduleNode.getElementsByClassName(elementClass);
// console.log(x);
var y;
for ( var i = 0; i < x.length; i++ ) {
// console.log(i);
y = x[i].getElementsByTagName(elementTag);
// console.log(y);
for (var k=0; k<y.length; k++){
// console.log(y[k]);
results.push(y[k]);
}
// console.log(results);
}
return results;
};
Any suggestions please?
Thanks.
this.getElementsByClassNameThenTagName = function (elementClass, elementTag) {
if (parentNode == null) {
this.init();
}
var results = {}; // thid should be an object (collection)
var x = moduleNode.querySelectorAll("." + elementClass + " " + elementTag);
x.forEach(function(y) {
var name = y.getAttribute("name"); // if you really sure that all the matched elements have names
results[name] = y;
});
return results;
};
Now you can use the results array like this:
var someElement = results['some name'];
NOTE: All the matched elements x should have a name attribute, and all the name attributes of the matched elements should be unique.
I have JavaScript "Class" that makes js objects smarter. I serialize these objects to localStorage and restore them. But these restored object are not instances of a class. Restored objects are just property-sets. So, I do set __proto__ of these objects.
And I've read that using __proto__ is wrong (slow and deprecated) approach to make objects smarter.
What is smart solution for these problem? How to restore JavaScript objects so that they will be instances of the corresponding classes?
var showMarks = function (locals) {
var myMarks = locals.myMarks = locals.myMarks.slice(0, 120);
for (var i = myMarks.length - 1; i >= 0; i--) {
myMarks[i].__proto__ = Mark.prototype;
}
}
showMarks(JSON.parse(localStorage.myMarks));
UPDATE According to comment, I added a replacement of original literal objects foreach myMarks
You could first instanciate your class, then loop through your object's properties to set them after instanciation
var mark, key;
for (var i = myMarks.length - 1; i >= 0; i--) {
mark = new Mark();
for (key in myMarks[i]) {
if (myMarks[i].hasOwnProperty(key)) {
mark[key] = myMarks[i][key];
}
}
// replace previous plain object with the new instance of Mark
myMarks[i] = mark;
}
I suppose you can add a property indicating the class they belongs to before storing them (ie: .__class).
Then, you can write a function (let's say restore(obj)):
function restore(obj){
switch(obj.__class){
case "Class1":
return new Class1(obj.a, obj.b);
}
return null;
}
The correct solution is to have a method to serialize and one to unserialize your instance. JSON.stringify/parse work well for plain objects, but if you want more, you have to make your custom methods.
Example with class that has private properties:
function Cls(name) {
var private_name = name; // private!
this.getName = function() { return private_name; }
}
Cls.prototype.sayHi = function() {
console.log(this.getName() + ' says hi!');
}
// serialization
Cls.prototype.toJSON = function() {
return JSON.stringify({ // add what you need to reconstruct the class in it's current state
name : this.getName()
});
}
Cls.fromJSON = function(str) {
return new Cls(JSON.parse(str).name); // you might need to have a special case in the constructor for this
}
// usage:
var c = new Cls('me');
var serialization = c.toJSON();
var clone = Cls.fromJSON(serialization);
clone.sayHi()
I might have written a pretty confusing title but my question is rather simple.
I'm looking for an efficient way to remove an item from an array. But my array is full objects that has been stringified (I'm writing my app on Node.js and I'm using JSON.stringify method). So my array is like this;
"{\"userID\":\"15\",
\"possibleFollowers\":[
{\"followerID\":\"201\",\"friends\":716},
{\"followerID\":\"202\",\"friends\":11887},
{\"followerID\":\"203\",\"friends\":11887}],
\"name\":\"John\",
\"lon\":\"Doe\"}"
My question is on Javascript(or Node). If I wanted to remove the from possibleFollowers with "followerID: 202", how would I be able to do that efficiently?
var string = "…";
var obj = JSON.parse(string);
obj.possibleFollowers = obj.possibleFollowers.filter(function(fol) {
return fol.followerID != "202";
});
string = JSON.stringify(obj);
var data = "{\"userID\":\"15\",\"possibleFollowers\":[{\"followerID\":\"201\",\"friends\":716},{\"followerID\":\"202\",\"friends\":11887},{\"followerID\":\"203\",\"friends\":11887}],\"name\":\"John\",\"lon\":\"Doe\"}";
var dataObject = JSON.parse(data);
dataObject.possibleFollowers = dataObject.possibleFollowers.filter(function(follower) {
return !(follower.followerID == "202");
});
data = JSON.stringify(dataObject);
In javascript, the splice method is used to delete an array element by index.
see :
http://www.roseindia.net/java/javascript-array/javascript-array-remove-index.shtml
try just to delete it by using "delete"
for (var i in possibleFollowers) {
if (possibleFollowers[i]['followerId'] == '216') {
delete possibleFollowers[i];
}
}
I need a way to add an object into another object. Normally this is quite simple with just
obj[property] = {'name': bob, 'height': tall}
however the object in question is nested so the following would be required:
obj[prop1][prop2] = {'name': bob, 'height': tall}
The clincher though, is that the nesting is variable. That is that I don't know how deeply each new object will be nested before runtime.
Basically I will be generating a string that represents an object path like
"object.secondObj.thirdObj.fourthObj"
and then I need to set data inside the fourth object, but I can't use the bracket [] method because I don't know how many brackets are required beforehand. Is there a way to do this?
I am using jQuery as well, if that's necessary.
Sure, you can either use recursion, or simple iteration. I like recursion better. The following examples are meant to be proof-of-concept, and probably shouldn't be used in production.
var setDeepValue = function(obj, path, value) {
if (path.indexOf('.') === -1) {
obj[path] = value;
return;
}
var dotIndex = path.indexOf('.');
obj = obj[path.substr(0, dotIndex)];
return setDeepValue(obj, path.substr(dotIndex + 1), value);
};
But recursion isn't necessary, because in JavaScript you can just change references.
var objPath = 'secondObj.thirdobj.fourthObj';
var valueToAdd = 'woot';
var topLevelObj = {};
var attributes = objPath.split('.');
var curObj = topLevelObj;
for (var i = 0; i < attributes.length; i++) {
var attr = attributes[i];
if (typeof curObj[attr] === 'undefined') {
curObj[attr] = {};
}
curObj = curObj[attr];
if (i === (attributes.length - 1)) {
// We're at the end - set the value!
curObj['awesomeAttribute'] = valueToAdd;
}
}
Instead of generating a string...
var o="object";
//code
o+=".secondObj";
//code
o+=".thirdObj";
//code
o+=".fourthObj";
...you could do
var o=object;
//code
o=o.secondObj;
//code
o=o.thirdObj;
//code
o=o.fourthObj;
Then you can add data like this:
o.myprop='myvalue';
And object will be updated with the changes.
See it here: http://jsfiddle.net/rFuyG/
I am building a playlist of songs in javascript. I have used an associative array foo -- the structure of my object looks akin to:
foo[songID] = songURL;
I am trying to build in shuffling functionality. I would like to select a song at random from this list. Is there a simple way to do this -- the array is not indexed.
You can use the function Object.keys(object) to get an array of the keys of an object. Very good documentation for this function can be found at MDN.
You also seem to have two different but related questions.
Your topic asks how to get a random element from an object. For that,
var randomProperty = function (object) {
var keys = Object.keys(object);
return object[keys[Math.floor(keys.length * Math.random())]];
};
But you also ask in the body of your question how to shuffle the array. For that, you'll want a shuffle function of some sort (most probably an implementation of Fischer-Yates), and do that directly.
var objectKeysShuffled = function (object) {
return shuffle(Object.keys(object));
};
This is the function I made to play a random background song from a hash of audio elements.
this.bgm = {} //I later added audio elements to this
this.playRandomBGM = function()
{
var keys = Object.keys(this.bgm);
self.currentBGM = keys[Math.floor(keys.length * Math.random())];
console.log("Playing random BGM: " + self.currentBGM);
self.bgm[self.currentBGM].play();
}
function fetchRandom(arr) {
var ret,
i = 0;
for (var key in arr){
if (Math.random() < 1/++i){
ret = key;
}
}
return ret;
}
var randomSong = foo[fetchRandom(foo)];
You should use an object for this. Then use an indexed array of objects for randomization, but this should answer your question.
An inefficient method is:
function randomProperty(obj) {
var a = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
a.push(p);
}
}
return a.length? obj[ a[a.length * Math.random() | 0]] : void 0;
}
As others have said, much more efficient store the property name array and re-use it than to make it each time.
You could try something like this:
var obj = {
'song1': 'http://...1',
'song2': 'http://...2',
'song3': 'http://...3',
'song4': 'http://...4',
'song5': 'http://...5',
'song6': 'http://...6',
}, tempArr = [], len, rand, song;
for ( var key in obj )
if ( obj.hasOwnProperty(key) )
tempArr.push( obj[key] );
len = tempArr.length;
rand = Math.floor( Math.random() * len );
song = tempArr[rand];
document.write(song);
Note that this is really just a hacky way of skirting around the fact that this stuff should probably be in an array with a structure like this:
var songs = [
{title: 'Song1', url: 'http://...1.mp3'},
{title: 'Song2', url: 'http://...2.mp3'},
{title: 'Song3', url: 'http://...3.mp3'}
];