I've just started getting into OOP and was wondering the correct structure for linking objects together.
Say for example I had an object called "Business":
function Business(name, sector, capital, employees, type, id) {
var self = {
name: name,
sector: sector,
capital: capital,
employees: employees,
stock: 0,
type: type,
id: id,
};
self.produce = function() {
return self.capital * self.employees;
}
Now in the example above using the properties capital and employees the method 'self.produce' produces output. This output I want to store depending upon the sector.
As a result, would I create a seperate "Sector" object? Or alternatively, within my business object, would it inherit the "Sector" object?
I understand inheritance is used to the attributes of similar objects for example a pet would inherit from an Animal. But in my case of "Business" and "Sector" I'm slightly confused.
In this case you want to use composition, business "has a" sector but a business "is not" a sector. Business would have an attribute sector, this is passed in the constructor so you can pass different sectors to different businesses. So if the output of produce depends on sector, the code would be in sector classes.
Related
I'm brand new to javascript (2nd week of learning) so I'm sorry if this is a stupid question! I've found 2 ways to do the thing I'm trying to do and need some advice on whether the second way is acceptable.
I know a class can have properties that take arrays where the array lists multiple values of that one property. However, if I have a class with properties that are group-able (relate to the same aspect of the object) can I make them into one property that takes an array rather than listing each as a property?
So, if I have a Tent class with a mainUses property, I know I can pass a new instance an array for a tent that has multiple main uses.
class Tent {
constructor(mainUses){
this.mainUses = mainUses;}};
const myTent = new Tent (["Backpacking","Mountaineering","Bikepacking"]);
And if I also have a minimum pack weight, standard weight and maximum weight for the tent I can do:
class Tent {
constructor(mainUses,minWeight,standardWeight,maxWeight){
this.mainUses = mainUses;
this.minWeight = minWeight;
this.standardWeight = standardWeight;
this.maxWeight = maxWeight;}};
const myTent = new Tent (["Backpacking","Mountaineering","Bikepacking"], "2kg","2.2kg","2.4kg");
But what about if I group the 3 weights into one 'super-property' (for want of a better description) like this:
class Tent {
constructor(mainUses,weight){
this.mainUses = mainUses;
this.weight = weight;}};
And then pass the 'super-property' an array listing the 3 weights:
const myTent = new Tent (["Backpacking","Mountaineering","Bikepacking"],["2kg","2.2kg","2.4kg"]);
And then, so that they can still easily be accessed, add a comment to the class listing the indices to use when I make a new instance or want to access something.
/*minWeight [0], standardWeight [1], maxWeight[2]*/
So that myTent.minWeight would become myTent.weight[0].
Is there any reason I shouldn't do it this way? It appears to work for what I'm trying to do but I'm worried that it's bad form/hacky/wrong-for-some-other-reason to use it like this?
I've tried to search for an example of it being used this way but I don't really know how to describe it succinctly enough to search effectively. The example I've used doesn't show it well but where I have lots of properties that I could group into a single array, it ends up being much simpler. I feel like it's semantically...off somehow?
You could do it like this, but if you have different properties in the same array it would maybe be better to use an associative array. In my opinion, it would be better organized. It would also be more object oriented and it would still be ok if you add more properties.
const myTent = [
{ item: 'Backpacking',
weight: '2kg',
},
{ item: 'Mountaineering',
weight: '2.2kg',
},
{ item: 'Bikepacking',
weight: '2.4kg',
},
];
Objects are generally used instead of arrays for this purpose because it is much easier to not get confused about which element means which thing when they are named.
// Weight property
{
min: 2,
standard: 2.2,
max: 2.4,
unit: 'kg',
}
I've been reading all around the MDN, but I get stuff like:
keyPath
The key path for the index to use. Note that it is possible to create an index with an empty keyPath, and also to pass in a sequence (array) as a keyPath.
Well, no s#!t, keyPath is a key path. But what is that?
In all examples, it's the same thing as the name of the column (or index, as they call it):
objectStore.createIndex("hours", "hours", { unique: false });
objectStore.createIndex("minutes", "minutes", { unique: false });
objectStore.createIndex("day", "day", { unique: false });
objectStore.createIndex("month", "month", { unique: false });
objectStore.createIndex("year", "year", { unique: false });
They say that you can pass:
An empty string - ""
A valid JavaScript identifier (I assume this means valid JS variable name)
Multiple javascript identifiers separated by periods, eg: "name.name2.foo.bar"
An array containing any of the above, eg.: ["foo.bar","","name"]
I can't imagine what purpose does this serve. I absolutely do not understand what keyPath is and what can I use it for. Can someone please provide example usage where keyPath is something else than the name of the column? An explanation what effect do values of keyPath have on the database?
Examples might help. If you use a key path with an object store, you can have keys plucked out of the objects being stored rather than having to specify them on each put() call. For example, with records that just have an id and name, you could use the record's id as a primary key for the object store:
store = db.createObjectStore('my_store', {keyPath: 'id'});
store.put({id: 987, name: 'Alice'});
store.put({id: 123, name: 'Bob'});
Which gives you this store:
key | value
------+-------------------
123 | {id: 123, name: 'Bob'}
987 | {id: 987, name: 'Alice'}
But if you want to look record up by name, you create an index:
name_index = store.createIndex('index_by_name', 'name');
Which gives you this index:
key | primary key | value
--------+-------------+--------------------------
'Alice' | 987 | {id: 987, name: 'Alice'}
'Bob' | 123 | {id: 123, name: 'Bob'}
(The index doesn't really store a copy of the value, just the primary key. But it's easier to visualize this way. This also explains the properties you'll see on a cursor if you iterate over the index.)
So now you can look up a record by name with:
req = name_index.get('Alice')
When records are added to the store, the key path is used to generate the key for the index.
Key paths with . separators can be used for lookups in more complex records. Key paths that are arrays can either produce compound keys (where the key is itself an array), or multiple index entries (if multiEntry: true is specified)
A great way to understand things is to think how you would design it yourself.
Let's take a step back, and look at how a very simple NoSQL database would store data, then add indices.
Each Store would look like a JavaScript object (Or dictionary in python, a hash in C# ) etc...
{
"1": {"name": "Lara", "age": "20"},
"2": {"name": "Sara", "age": "22"},
"3": {"name": "Joe", "age": "22"},
}
This structure is basically a list of values, plus an index to retrieve the values, like so:
//Raw JS:
var sara = data["2"]
// IndexedDB equivalent:
store.get(2)
This is super fast for direct object retrieval, but sucks for filtering. If you want to get people of a specific age, you need to loop over every value.
If you knew in advance you would be doing your queries by age, you could really speed up such queries by creating a new object which indexes the value by age:
{
"20": [<lara>],
"22": [<joe>, <sara>],
...
}
But how would you query that? You can't use the default indexing syntax (e.g. data[22] or store.get(22)) because those expect the key.
One way would be to name that second index, e.g. by_age_index and give our store object a way to access that index by name, so you could to this:
store.index('by_age_index').get(22) // Returns Joe and Sara
The last bit of the puzzle would be telling that index how to determine which records go against which key (because it has to keep itself updated when records are added/changed/removed)
In other words, how does it know Joe and Sara go against key 22, but Lara goes against key 20?
In our case, we want to use the field age from each record. This is what we mean by a keyPath.
So when defining an index, it makes sense that we would specify that as well as the name, e.g.
store.createIndex('by_age_index', 'age');
Of course, if you want to access your index like this:
store.index('age').get(22) // Returns Joe and Sara
Then you need to create your index like this:
store.createIndex('age', 'age');
Which is what most people do, which is why we see that in examples, which gives the impression that the first argument is the keyPath (whereas it's actually just the arbitrary name we give that index) leaving us unsure about what the second argument might be for.
I could have explained all this by saying:
The first parameter is the handle by which you access the index on the store, the second parameter is the name of the field on the record by which that index should group its records.
But maybe this rundown will help other people too :-)
A keypath is how you indicate to indexedDB which properties of your object play a special role. Similar to how you would indicate to an SQL database that a certain column in a table is the primary key of the table, or how you could tell a database to create an index on one or more particular columns in a table.
In other words, it is the path that the indexedDB implementation should follow when determining which property should be used for some calculation. For example, when searching for a value with a given key.
It is a path, and not a simple key, because it considers that object property values can also be objects. In other words, there is a hierarchy. For example, {a:{b:1}}. The "path" to the value 1 is "a.b". The path is the sequence of properties to visit to get to the value.
The key part of the name signifies that the columns play an important role. For example, in identifying the primary key property, or a particular indexed property.
Properties that are not part of the keypath are ignored in the sense that the indexedDB implementation just treats the whole object as a bag of properties, and only pays attention to those, or gains awareness of those, that are a part of a keypath.
I am working on a JavaScript application where I have to access a big object at many places. I am wondering what is the better way to go for it in terms of time and space optimisation.
For example, let's say I have on object (this is just an example, my objects will be much bigger and deeply nested):
family: {
person: {
firstName: Sherlock,
lastName: Holmes,
age: 22,
address: {
building: 221B,
addressLine1: Baker Street,
city: London,
}
}
}
Now, if I want to access the address here and print it on my webpage, then what is the good approach:
Shall I do this:
<div>{family.person.address.building}</div>
<div>{family.person.address.addressLine1}</div>
<div>{family.person.address.city}</div>
or, I store them in consts and then display here like:
const address = family.person.address
const {
building, addressLine1, city
} = address
<div>{building}</div>
<div>{addressLine1}</div>
<div>{city}</div>
In the former one, I am not creating new variable but traversing the object in depth each time I have to access something.
In the latter one, I don't have to do that traverse but have created 4 new variables
I feel second one is the fast approach (takes less time) because object is not traversed multiple times but I didn't find any articles supporting it.
Which approach will give me better time or space optimisation in my application?
In our application currently we are passing an array of objects from the server side into the model, and each element in the array has a key in it. For instance...
[
{name: "dog", sound: "bark", food: "cats"},
{name: "cat", sound: "meow", food: "mouse"}
]
In the model this is defined like so...
animals: hasMany(Animal, {key: 'name', embedded: true })
Then if we want the data for the cat, we use the findBy feature to find the one with the name = "cat" like so...
var animalName = "cat";
model.get('animals').findBy('name', animalName);
This works well, there are lots of potential types of 'animal' objects, and we already know what we're looking for.
However I'm curious for other reasons if we can pass this in as a Map from the server, which becomes a JSON object on the client that looks like this...
animals : { "dog" : {sound: "bark", food: "cat"},
"cat" : {sound: "meow", food: "mouse"}
}
It seems that in order to do this, in the model code we need to define a "has a" relationship for each potential animal type, which is dynamic and we do not want to hard code all the options here. Is there a way to say animals hasMany animals, but there in a map by name, rather then just an array?
I never figured out how to do this with inheritance the way I wanted, so I ended up defining a animals as a basic attribute...
animals: attr(),
Then putting my object in that and accessing the data with
animals.dog.sound
or
var name = "dog";
animals[name].sound
and this works, although we lose some benefits of the model inheritance system. For this application the inheritance wasn't that important.
I have created a data file using JSON. The data is a selection of variables about tube stations. See below for a section of the file.
This setup works very well for finding stations when you have the station id.
example: when you click on a station on my webpage the elements id is the station code and this allows a popup box to be filled with the stations name and zone.
However, how would it be best to find the location of the station when for example given the full text name?
a) I would have to have a code iterating through every station object and using an if command check the name in the object against the name given.
b) created a set of key:value pairs in a second javascript object which is first used to lookup the station id then searches in the first object for the rest of the station data.
Also how would I find data on all the stations on a given line.
a) iterate through and check for each that the tubelines array contains the line string
b) Yet another object containing all station codes under a line heading.
{
"BST": {
"name": "Baker Street",
"Location": [
51.523129639184,
-0.15688978273689
],
"zone": 1,
"tubelines": [
"Bakerloo",
"Circle",
"Hammersmith & City",
"District"
],
"Bakerloo": {
"stepfreetrain": false
},
"Circle": {
"stepfreetrain": false
},
"Hammersmith & City": {
"stepfreetrain": false
},
"District": {
"stepfreetrain": false
}
},
"CHX": {
"name": "Charing Cross",
"Location": [
51.508358644804,
-0.12478853109718
],
"zone": 1,
"tubelines": [
"Bakerloo",
"Northern"
],
"Bakerloo": {
"stepfreetrain": true
},
"Northern": {
"stepfreetrain": true
}
}
}
So the key question is, is this the best way to lay out the data above and if so which is the quickest method to pull out information?
To answer your question (was too long to post it as comment):
Create these secondary objects within your "initialization" phase. After generating the objects you can make them accessible via a simple "API". Made a small example (using jQuery and assuming your spots are stored in a spots.json file):
var myApp = {
init: function() {
// show loading screen while fetching data and creating the "lookup" objects
showLoader();
var loadSpots = $.ajax({
url: "/spots.json"
});
loadSpots.done(function(spots) {
myApp.spots = spots;
$.each(spots, function(code, details) {
nameLookup[details.name] = spots[code];
});
// All done, hide the loading screen
hideLoader();
});
},
spots: {},
nameLookup: {},
findByCode: function(code) {
return myApp.spots[code];
},
findByName: function(name) {
return myApp.nameLookup[name];
}
};
myApp.init();
// you can then use these API methods to find all information for Baker Street either by name or by code (or by whatever)
myApp.findByCode('BST');
myApp.findByName('Baker Street');
Iterating through one only object: slower
Creating multiple objects with different layouts: redundancy (thus possibly error-prone)
If speed of reading the data is more important than high(er) memory consumption write a function to generate several objects designed for your needs at runtime automatically, ONCE. Don't do that manually since it's error-prone and a monkey task. And don't forget to observe changes to your main object (if it can be modified at runtime) so you can regenerate your optimized objects as soon as an entry gets modified. That costs some performance once when starting your application but then you can read from the generated objects fast and easily.
If low memory consumption is more important than speed of finding entries (for example because your application is running on older devices with low memory) just iterate through the entries. I'm not 100% sure if one iteration per lookup uses less memory than creating several different objects and keeping them in memory - it probably depends on the size of your data object.
Looking up keys on the first level of an object is of course always faster than iterating and searching for values deep within an object but it uses more memory since the additional objects must be stored somewhere.
I would suggest to put the data of all the separate tube lines inside the "tubelines" property.
For example:
{
"tubelines": {
"Bakerloo": {
"stepfreetrain": true
},
"Northern": {
"stepfreetrain": true
}
}
}
That makes looking up data easier as you can traverse the JSON "tree" for each tube line and find it's data.
Also, I find it easier to name the coordinates "lat" and "long" as separate properties of "Location" (Location.lat, Location.long) vs using an array (Location[0], Location[1]) for readability and eliminating possibility of making mistakes when entering data.
I would suspect that given the number of tube stations (270?) that you're not going to notice any particular data arrangement being superior. A simple iteration would be barely noticeable on virtually any platform.
I would rather (as ever) concentrate on making the code legible and easy to manage/maintain, and worry about specific performance issues as they arise.