Example of SchoolCatalog class

In regards to the School Catalog exercise, the SchoolCatalog class is not an extension of the School Class.

It should have objects of schools. How does that work?

Can you show what the SchoolCatalog class should look like ?

Many thanks!

2 Likes

Could you give me a link to your problem? I could help you more if you showed me it.

1 Like

Hi ,
here is the link : https://www.codecademy.com/courses/introduction-to-javascript/projects/school-catalog

The questions is about the additional execercise under point 19. : * Create a class called SchoolCatalog that holds a collection of schools. Create an instance of SchoolCatalog for primary, middle, and high schools.

I am trying to put together that class, but am stuck on the fact that it should hold ‘school objects’. Can you show me what that class should look like?

Many thanks

2 Likes

Sorry, but I have not gotten to that lesson, so I just found out it won’t let me in. Please type the directions I guess, and then I will be able to help you. Sorry for complicating things :sob:

We want three instances of the SchoolCatalog class…

class SchoolCatalog {

}

primary = new SchoolCatalog();
middle = new SchoolCatalog();
high = new SchoolCatalog();

That’s the skeleton. Now we need to visualize the data that goes into each instance. The idea would appear to be storing an array of schools in each instance so that it can polled and printed.

Question: Should this class need a constructor method? Or is a class variable adequate?

class SchoolCatalog {
  _list = [];
  get list () {

  }
  addSchool (school) {

  }
}

This is not a how-to, but a sketch to help organize our ideas. What if we want the array to consist of School objects?

One consideration would be to create the instance of a School, first, then pass that object to the SchoolCatalog instance that applies to its level. For now this will be a manual process.

2 Likes

Let’s insist upon get list() returning the full list of objects. This way we can use the getter in our instance methods be able to drill down into each object.

return this._list;

In answer to the question about a constructor, turns out we do need one.

constructor () {
  this._list = [];
}

In the addSchool method, we are given a School object from an outside instance of that class. The new array element will be a reference to that object. The reference added to the SchoolCatalog instance is not a copy, but a reference to that actual instance. In other words, we’re not creating data, only accessing it.

The data in this class is merely a pointer to data in the School class and its subsidiaries.

    this.list.push(school);    // uses the getter

Remember quickFacts? We can write our own for this class.

  quickFacts(x) {
    console.log(
      `${x.name} educates ${x.numberOfStudents} ${x.level} school students.`
    );
  }
1 Like

Hi mtf,
Thank you for clarifying, much appreciated.
PS It might be useful to add the how-to of that particular class (SchoolCatalog) to the tutorial, because it touches OOP logic that was not yet explained.

2 Likes

As stated earlier, this is a sketch, but we can rustle up some trial code to help give us some direction.

Start with some HighSchool instances…

const alSmith = new HighSchool(
  'Al E. Smith',
  415,
  ['Baseball', 'Basketball', 'Volleyball', 'Track and Field']
);
const anHenday = new HighSchool(
  'Anthony Henday',
  515,
  ['Baseball', 'Basketball', 'Volleyball', 'Track and Field', 'Hockey']
);
const salisbury = new HighSchool(
  'Salisbury',
  315,
  ['Baseball', 'Basketball', 'Volleyball', 'Track and Field', 'Hockey']
);

Given this class definition,

SchoolCatalog
class SchoolCatalog {
  constructor () {
    this._list = [];
  }  
  get list () {
    return this._list;
  }
  addSchool (school) {
    this.list.push(school);
  }
  quickFacts (x) {
    console.log(
      `${x.name} educates ${x.numberOfStudents} ${x.level} school students.`
    );
  }
  print () {
    this.list.forEach(this.quickFacts);
  }
}

and the three instances mentioned,

primary = new SchoolCatalog();
middle = new SchoolCatalog();
high = new SchoolCatalog();

we can now load up the high school instance…

high.addSchool(alSmith);
high.addSchool(salisbury);
high.addSchool(anHenday);

and now we can print all the quickFacts…

high.print();
Al E. Smith educates 415 high school students.
Salisbury educates 315 high school students.
Anthony Henday educates 515 high school students.

This is still very basic, as we need to give our class some intuition so schools of one level cannot be added to the list of another. That logic will take some thought.

const lorraineHansbury = new PrimarySchool(
  'Lorraine Hansbury',
  514,
  'Students must be picked up by a parent, guardian, or a family member over the age of 13.'
);
high.addSchool(lorraineHansbury);
LevelMismatchError: Cannot insert Lorraine Hansbury school into the list. 

[ Edited to reveal more logic. 181218 1828h UTC -7h ]

3 Likes

Hi mtf,
Great, I understand now.
Thank you so much for taking the time to clarify, much appreciated.
Best
Marco

3 Likes

Please let us know when you have the logic to produce the above output.

Al E. Smith has 415 high school students.
  Intramural sports programs:
  Baseball, Basketball, Volleyball, Track and Field
Salisbury has 315 high school students.
  Intramural sports programs:
  Baseball, Basketball, Volleyball, Track and Field, Hockey
Anthony Henday has 515 high school students.
  Intramural sports programs:
  Baseball, Basketball, Volleyball, Track and Field, Hockey

or should I say, all the above notwithstanding?

Hi Roy,

Thank you for following up.

This is what I put together

class SchoolCatalog{
  constructor(){
    this._list = [];
  }
  get list(){
    return this._list;
  }
  addSchool(school){
    this._list.push(school);
  }
  quickFacts(x){
    const teams = (x.sportsTeams ? `Intramural sports programs ${x.sportsTeams}` : '');
    console.log(`${x.name} educates ${x.numberOfStudents} ${x.level} school students. ${teams}`);
  }
  print(){
    this._list.forEach(this.quickFacts);
  }
}
class School {
  constructor(name, level, numberOfStudents){
    this._name = name;
    this._level = level;
    this._numberOfStudents = numberOfStudents;
  }
	get name(){
    return this._name;
  }  
  
  get level(){
    return this._level;
  }
  
  get numberOfStudents(){
    return this._numberOfStudents;
  }
  
  set numberOfStudents(value){
    if(value.isNaN){
      console.log('Invalid input: numberOfStudents must be set to a number.');
    }
    else{this._numberOfStudents = value;}
  }
	
  quickFacts(){
    console.log(`${this.name} educates ${this.numberOfStudents} students of the ${this.level} school level.`);
  }
  
  static pickSubstituteTeacher(substituteTeachers){
    const randomIndex = Math.floor(Math.random()*substituteTeachers.length);
    console.log(substituteTeachers[randomIndex]);
  }
 }

class PrimarySchool extends School {
  constructor(name, numberOfStudents, pickupPolicy){
    super(name, 'primary', numberOfStudents);
    this._pickupPolicy = pickupPolicy;
    
  }
  get pickupPolicy(){
    return this._pickupPolicy;
  }
  
}
  
class HighSchool extends School {
    constructor(name, numberOfStudents, sportsTeams){
      super(name, 'high', numberOfStudents);
      this._sportsTeams = sportsTeams;
    }
    
    get sportsTeams(){
    return this._sportsTeams;
    }
  }

/*
School.pickSubstituteTeacher(['Jamal Crawford', 'Lou Williams', 'J. R. Smith', 'James Harden', 'Jason Terry', 'Manu Ginobli']);
*/

const alSmith = new HighSchool('Al E. Smith', 415, ['Baseball', 'Basketball', 'Volleyball', 'Track and Field']);

const anHenday = new HighSchool(
  'Anthony Henday',
  515,
  ['Baseball', 'Basketball', 'Volleyball', 'Track and Field', 'Hockey']
);
const salisbury = new HighSchool(
  'Salisbury',
  315,
  ['Baseball', 'Basketball', 'Volleyball', 'Track and Field', 'Hockey']
);


primary = new SchoolCatalog();
middle = new SchoolCatalog();
high = new SchoolCatalog();

high.addSchool(alSmith);
high.addSchool(anHenday);
high.addSchool(salisbury);
/*
high.quickFacts(alSmith);
high.quickFacts(anHenday);
high.quickFacts(salisbury);
*/
high.print();
1 Like

Have you figured out the logic to prevent inserting a School object into the wrong list?

Both of those lines can call upon the getter for the list.

this.list.push(...)

print (this.list.forEach(...)

Hi Roy,

Yes, thank you for pointing that out. It should be without underscore outside of constructor, getter or setter, right?

Marco

I wouldn’t go so far as to say, “should”, but “can”. Getters are not just for accessing the backing variable from the outside, but for other methods, as well. No point having a getter if it is never used.

The less instances of direct access to the backing variable the better, from my view. Note that addSchool is not a setter, only a method for adding data to the list, so there are no circular references. A setter would naturally need to access the backing variable, but that is for another discussion.

Still interested to see how you handle the list insertion restriction. Could you use a hint?

In regards to the insertion restriction, I have been trying to find the solution (with instanceof,…)
But cannot get my head around it.
Could use a hint, yes :wink:
Many thanks.

I appreciate how hard you are working on this problem, and expect you are familiar enough with the existing logic to make sense of this hint:

constructor(level) {
  this._list = [];
  this._level = level;
}

Give it a getter to access the backing variable with this.level.

Now we have a state to test in both the list insertion step of addSchool and when printing the quickFacts.

Interested in how you complete these steps, so keep us posted.

1 Like

Thank you for that, very helpful.
I have modified the SchoolCatalog as follows;

class SchoolCatalog{
  constructor(level){
    this._list = [];
    this._level = level;
  }
  get list(){
    return this._list;
  }

  get level(){
      return this._level;
  }

  addSchool(school){
    if(this.level == school.level){this.list.push(school);}
    else{console.log(`Oops! You tried to add a ${school.level} school. This list accepts ${this.level} schools only.`)}
    
  }

  quickFacts(x){
    const teams = (x.sportsTeams ? `Intramural sports programs ${x.sportsTeams}` : '');
    console.log(`${x.name} educates ${x.numberOfStudents} ${x.level} school students. ${teams}`);
  }
  print(){
    this.list.forEach(this.quickFacts);
  }
}

I have tested with correct and incorrect instances and the outcome is positive.

I am not sure why a test is needed in quickFacts as well, though. Can you clarify?

We can test level instead of sportsTeams, for greater simplicity. A straight if statement will do the trick (as opposed to the verbose ternary).

1 Like

I understand now.
I have rewritten quickfacts(x) as follows .

 quickFacts(x){
   
   if(this.level == 'primary'){
    const addition = `The policy is: ${x.pickupPolicy}`;  
     }
   else if(this.level == 'high'){
    const addition = `Intramural sports programs ${x.sportsTeams}`;  
   }
  
    console.log(`${x.name} educates ${x.numberOfStudents} ${x.level} school students. ${addition}`);
  }
3 Likes

@mtf, @marco.los
I’ve been following your code through, as I used the same method to build the SchoolCatalog class, and to verify the level of school added using the addSchool() method.

Although I didn’t add the quickFacts() method to my version, I’m interested in how it works; but I don’t understand the use of the ‘x’ parameter: quickFacts(x)

Does ‘x’ stand for whatever school object is passed into the method? If that’s the case, then what’s the point of having this method in the SchoolCatalog class, as it wouldn’t be referencing the schools already stored in the this._list array. Shouldn’t quickFacts() be iterating through the this._list array and either printing a list of facts for all the schools at a particular level, or for a particular sample, perhaps also using .filter()?