Javascript - OOP reference loop - javascript

I trying to create Object-"Room" that have another objects-"Items".
I want that every "Item" will have a reference to "Room".
I just want to know what is the "Room" of some "Item"...
Here is the problem :
// Creating Room Class
function Room(name){
this.name = name;
this.items = [];
};
// function that creates item in a Room
Room.prototype.addItem = function(name){
var newItem = new Item(name, this);
this.items.push(newItem);
}
// Creating Items Class
function Item( name, room ){
this.name = name;
this.parentRoom = room; // HERE IS THE PROBLEM!!
// I need reference to my Room Class
};
// Lets create a Room
var myRoom = new Room('MyRoom');
// And lets add some items
myRoom.addItem("chair");
myRoom.addItem("bed");
myRoom.addItem("computer");
// Result -> you can see endless loop of Rooms in th Items
console.log( myRoom );
You can see in Inspect element endless loop.
Screenshot from Inspect element
Or what is the right way to create this thing?
I need to use "extends" method for "Item" Class to "Room"?

There is nothing wrong with un-ending object references when you inspect an javascript object. They essentially form a cyclic graph in object references.
However, this means that you get the benefits AND shortfalls of cyclic graphs.
When traversing/iterating through the graph, You need to be careful of infinite loops by keeping track of what objects you've already visited and do not visit them more than once. (i.e. by keeping track of a visited map)

Related

Array of objects: recommended to select specific item via objects key or Array Index or the Array item itself?

I am dealing with an Array[] of Class Objects. The requirement is to use that array of objects to remember the currently selected object. For example, I have a class called Person, and contacts is the array of the var obj = new Person(); contacts.push(obj);
class Person(){
// props, constructors and methods
}
const contacts = [new Person(0), new Person(1), new Person(2), new Person(3)]
var selectedPerson;
So I want the user to select any person from the contact list so I am using <select> and <option> for the user to allow selection of any Person This means I am using name props of the Person so I am getting name of the Person from the user selections now when a user selects or does anything that needs interaction with the Person's object or using that doing few calculations for the user and shows the results.
I can use the onChange event on the <select>
onChange(e){
// store the value and filter it out to get the Person object when need calculation
selectedPerson = e.target.value
// store the contact item which is person object and use them everywhere
selectedPerson = contacts.find((person)=> person.name===e.targe.value)
}
// multiple use of the selectedPerson
Which is the better way and why?
Is there any other way to achieve the same more efficiently?
Please share thoughts that are not asked properly on the question.
What you're doing is fine provided name is unique in the array of Person objects and the array isn't truly massive (which it won't be if it's feeding into option elements). Using name rather than array index is also more robust if the array contents change.
You might consider using a Map instead of an array:
const contacts = new Map();
function addContact(person) {
// If you want proactive notification of *replacing* a person, uncomment:
// if (contacts.has(person.name)) {
// throw new Error(`contacts map already contains a person named ${person.name}`);
// }
contacts.set(person.name, person);
}
addContact(new Person(0));
addContact(new Person(1));
// ...
Then in onChange:
onChange(e) {
const person = contacts.get(e.target.value);
// ...
}
That's just a more efficient retrieval than an array scan (but it's not going to matter with the number of people you're going to present in option elements) and, to me, semantically a bit more of a fit for the operation — there's no chance of duplicated names. If you need to loop through the entries in the map (in the order they were added), maps are iterable:
for (const person of contacts.values()) {
// ...
}
You can use Map class:
const people = new Map()
people.set('Jane', new Person('Jane'))
people.set('John', new Person('John'))
people.has('Jane') // true
people.has('Jack') // false
console.log(people.get('Jane'))

Javascript / Node Js - Create New variable for each instance

I'm creating a game bot on telegram using node js.
Currently I'm facing a problem on shared variable (module.exports). I'm storing some of the data on the variable. And the problem is, the shared variable index always change. For example, please refer to my code below
var sharedVar = [];
createNewRoom = function(res) {
var index = sharedVar.length;
sharedVar.push({ groupId : res.chat.id }); // every time this function is invoked, it will create a new array inside sharedVar object
//Here comes the problem, it's about the index,
//because I'm using sharedVar to store arrays, then it will become a problem,
//if one array is deleted (the index will change)
var groupId = sharedVar[index].groupId; // it runs OK, if the structure of array doesn't change, but the structure of array change, the index will be a wrong number
}
As you can see, i got callGameData function, when i call it, it will show the last value of sharedVar, it's supposed to show the current room values / data.
As i mention on the code above, it's all about the dynamic array in the sharedVar object, the index will change dynamically
Any thoughts to tackle this kind of issue? I was thinking about using a new sharedVar object everytime the createNewRoom function is invoked, but the thing is, i have to use sharedVar in many different function, and i still can't figure it out on using that method.
EDIT
This is the second method
var gameData = undefined;
createNewRoom = function() {
this.gameData = new myConstructor([]); // it will instantiate a new object for each new room
}
myConstructor = function(data) {
var _data = data;
this.object = function() {
return _data;
}
}
callGameData = function() {
console.log(gameData);
}
An array is fundamentally the wrong data type to use if you want to keep indices the same even in the face of removing entries.
A better method is to use properties of an object. For example:
var roomCache = { nextId: 1 };
createNewRoom = function(res) {
roomCache[roomCache.nextId++] = {groupId: res.chat.id}; // Add a new object to the cache and increment the next ID
}
After adding two elements, you'll have the rooms in roomCache[1] and roomCache[2] - if you want to start at zero just change the original value of nextId. You can delete elements in this object and it won't shift any keys for any other objects - just use delete roomCache[1] for example to get rid of that entry.
This assumes there isn't a better ID out there to use for the cache - if, for example, it made more sense to lookup by res.chat.id you could certainly use that as the key into roomCache rather than an auto-incrementing number. Here's how it would look like to cache the values by the group ID instead:
var roomCache = { };
createNewRoom = function(res) {
roomCache[res.chat.id] = {groupId: res.chat.id}; // Assumes res.chat.id is not a duplicate of an already cached obhect
}
Now you could just look up by group ID in the cache.
Yes, it's definitely a problem cause you are not keeping track of the index in a logical way, you are relying on position on the array which it changes, you need something that doesn't change over time to keep consistency and supports deletition of the element without affecting the rest of the elements. You could use mongo to store the generated rooms by id or maybe redis or some kind of key value pair database to store that kind of information.

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

Saving/Loading a complex object structure in Javascript (Node.js)

I have been coding in javascript for some time, but am fairly new to Node. I recently undertook a project that involves a complex object structure with multiple levels of prototypical inheritance and sub objects. This structure needs to be periodically saved / loaded. Saving and loading in JSON is desirable.
The Question
Is there a more elegant way of accomplishing the task of saving/loading these complex Javascript objects than my current method (outlined below)? Is it possible to design it in such a way where the constructors can initialize themselves as if they were normal objects without being bound by all of the restoring functionality?
My Solution
The base 'class' (from which, by design, all other objects under consideration inherit protoypically) has a function which processes an 'options' argument, adding all of it's properties to the current object. All deriving objects must include an options argument as the last argument and call the processing function in their constructor.
Each object also must add it's function name to a specific property so that the correct constructor function can be called when the object needs to be rebuilt.
An unpack function takes the saved object JSON, creates a plain object with JSON.parse and then passes that object in as the 'options' argument to the object's constructor.
Each object is given a unique id and stored in a lookup table, so that a function under construction with links to other objects can point to the right ones, or create them if it needs to.
Here is a plunker which demonstrates the idea (obviously in a non-Node way).
If you don't want to load the plunker, here's an excerpt which should hopefully provide the gist of what I'm trying to do:
function BaseClass(name, locale, options){
if(name) this.name = name;
if(locale) this.locale = locale;
// If options are defined, apply them
this.processOptions(options);
// create the classList array which keeps track of
// the object's prototype chain
this._classList = [arguments.callee.name];
// Create a unique id for the object and add it to
// the lookup table
if(!this.id) this.id = numEntities++;
lookupTable[this.id] = this;
if(!this.relations) this.relations = [];
// other initialization stuff
}
BaseClass.prototype = {
processOptions: function(options) {
if(options && !options._processed){
for(var key in options){
if(options.hasOwnProperty(key)){
this[key] = options[key];
}
}
options._processed = true;
}
},
addNewRelation: function(otherObj){
this.relations.push(otherObj.id);
}
// Other functions and such for the base object
}
function DerivedClassA(name, locale, age, options){
if(age) this.age = age;
this.processOptions(options);
if(options && options.connectedObj){
// Get the sub object if it already exists
if(lookupTable[options.subObj.id]){
this.subObj = lookupTable[options.subObj.id];
}
// Otherwise, create it from the options
else {
this.subObj = new OtherDerivedClass(options.subObj);
}
}
else {
// If no options then construct as normal
this.subObj = new OtherDerivedClass();
}
// If something needs to be done before calling the super
// constructor, It's done here.
BaseClass.call(this, name, locale, options);
this._classList.push(arguments.callee.name);
}
DerivedClassA.prototype = Object.create(BaseClass.prototype);
As mentioned, this gets the job done, but I can't help but feeling like this could be much better. It seems to impose a ridiculous amount of restrictions on the inheriting 'classes' and how their constructors must behave. It makes a specific order of execution critical, and requires that each object be deeply involved and aware of the restoration process, which is far from ideal.

reinitializing javascript object's properties

In my Javascript drag and drop build app, a variety of buildings can be built. The specific characteristics of these are all saved in one object, like
var buildings = {
house: ['#07DA21',12,12,0,20],
bank: ['#E7DFF2',16,16,0,3],
stadium: ['#000000',12,12,0,1],
townhall: ['#2082A8',20,8,0,1],
etcetera
}
So every building has a number of characteristics, like color, size, look which can be called as buildings[townhall][0] (referring to the color). The object changes as the user changes things. When clicking 'reset' however, the whole object should be reset to its initial settings again to start over, but I have no idea how to do that. For normal objects it is something like.
function building() {}
var building = new building();
delete building;
var building2 = new building();
You can easily delete and remake it, so the properties are reset. But my object is automatically initialized. Is there a way to turn my object into something that can be deleted and newly created, without making it very complicating, or a better way to store this information?
You can keep initial state as a prototype on an object.
var Base = function(){};
Base.prototype={a:1,b:2};
var c = new Base();
Now, you can change value of a or b to whatever you want.
To restore, just use
delete c.a or delete c.b
You will get your initial value back.
Hope this help.
Just use a copy/clone method to restore the original state
var defaults = {
foo: "bar"
};
var building;
function reset(){
building = {};
for (var key in defaults) {
if (defaults.hasOwnProperty(key){
building[key] = defaults[key];
}
}
}
Now you can just call reset() whenever you need to have building reset.

Categories