Project: Build a Library, step 25, how to "create a class called Catalog"?

Hello everyone,
Can someone help me with implementing this feature from the “Build a Library” project, step #25:

“Create a class called Catalog that holds all of the Media items in our library.”

I am completely at loss here, not even the slightly idea :scream_cat:
A corresponding article @MDN or a hint would be very helpful,
Thank you :+1:

This is my current implementation:

class Media {
  constructor(title) {
    this._title = title;
    this._isCheckedOut = false;
    this._ratings = [];
	}
 	get title() {
    return this._title;
  }
  get isCheckedOut() {
    return this._isCheckOut;
  }
  get ratings() {
    return this._ratings;
  }
  set isCheckOut(checked) {
		this._isCheckedOut = checked;
  }
  toggleCheckOutStatus() {
    this._isCheckedOut = !this._isCheckedOut;
  }
  addRating(rating) {
    if(rating > 5) {
      console.log('Rating between 1 and 5');
      return `Rating between 1 and 5`;
    }
    this._ratings.push(rating);
  }
  getAverageRating() {
    const ratings = this._ratings;
    const lengthOfArray = ratings.length;
        if (ratings.length === 0) {
          console.log('no ratings here!');
          return `no ratings here!`
        };
		const reducer = (accumulator, currentValue) => accumulator + currentValue;
    const average = ratings.reduce(reducer) / lengthOfArray;
    return `${this._title}'s average rating: ${Math.floor(average)}/5 - ${lengthOfArray} ratings`;
  }
}

class Book extends Media {
  constructor(title, author, pages) {
    super(title);
 		this._author = author;
    this._pages = pages;   
  }
  get author() {
    return this._author;
  }
  get pages() {
    return this._pages;
  }
}

class Movie extends Media {
  constructor(title, director, runTime) {
    super(title);
    this._director = director;
    this._runTime = runTime;  
  }
  get director() {
    return this._director;
  }
  get runTime() {
    return this._runTime;
  }
}

class Cd extends Media {
  constructor(title, artist, songs) {
    super(title);
    this._artist = artist;
  	this._songs = songs;
  }
  get artist() {
    return this._artist;
  }
  get songs() {
    return this._songs;
  }
  shuffle() {
    const a = this._songs;   
    for (let i = a.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [a[i], a[j]] = [a[j], a[i]];
    }
    return a;
  }
}

Build a Library

Thank @mtf, I’ve edited my post by adding the link you submitted.
Any idea on how to produce a class containing objects created by other classes ?

you have done this before, using extends, how come you are struggling now?

Oh, so is this simply about creating on top of Media a new root/parent class called Catalog, then extending Media out of Catalog ?

From the way I read the instructions, I am expecting Catalog to be some sort of object filled with all the instances (books, cds, movies) created in the previous steps, an object that I could console.log to display an actual catalog to the console.

1 Like

First time they use Media items in the instructions, how nice. So the question is, what is items referencing to?

The catalog seems to be designed to hold all the instances indeed, @mtf what do you think?

Haven’t really given this a lot of thought, but one would expect the Catalog class to be a standalone companion to the media library. With each new media item instance, a parallel instance is created in the catalog referencing title, media, and other pertinent cataloging data such as subject, catalog number, when ascessioned, when discarded, and so on.

1 Like
/* Parent Class */
class Media {
  constructor(title) {
    this._title = title;
    this._isCheckedOut = false;
    this._ratings = [];
  }
  
  get title() {
    return this._title;
  }
  
  get isCheckedOut() {
    return this._isCheckOut;
  }
  
  get ratings() {
    return this._ratings;
  }
  
  set isCheckedOut(checkedOut) {
    this._isCheckOut = checkedOut;
  }
  
  
  toggleCheckedOutStatus() {
    this._isCheckedOut = !this._isCheckedOut;
  }
  
  
  getAverageRating() {
    
    let  ratingsSum = this._ratings.reduce((currentSum, rating) => currentSum + rating, 0);
    let  ratingsLength = this._ratings.length;
    
    let  average = ratingsSum/ratingsLength;
    
    this._ratings = average;
    
    return this._ratings;
  
  }
  
  addRating(ratings) {
    if(ratings < 1 || ratings > 5 ) {
    console.log('Error! Please rate between 1 and 5')
         
    } else { 
       this._ratings.push(ratings); 
  }

  }
  
}

class Book extends Media{
  constructor(author, title, pages) {
    super(title);
    this._author = author;
    this._pages = pages;
  }
  
  get author() {
    return this._author;
  }
  
  get pages() {
    return this._pages;
  }
}

class Movie extends Media {
  constructor(director, title, runTime, movieCast) {
    super(title);
    this._director = director;
    this._runTime = runTime;
    this._movieCast = movieCast;
  }
  
  get director() {
    return this._director;
  }
  
  get runTime() {
    return this._runTime;
  }
}

class Cd extends Media {
  constructor(title, artist, songs) {
    super(title);
    this._artist = artist;
    this._songs = [];
  }
  get artist() {
    return this._artist;
  }
  
  get songs() {
    return this._songs;
  }
  
  addSong(songs) {
    this._songs.push(songs);
  }
    
  shuffle(songs) {
    for (var i = songs.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = songs[i];
        songs[i] = songs[j];
        songs[j] = temp;
    }
    return songs;
  }
}

class Catalog {
    constructor(mediaList){
    this._MyCatalog = [mediaList];
   }
  
get mediaList() {
     return this._mediaList;
  }
  
set mediaList(newMedialist)  {
   this._MyCatalog.push(newMedialist);
   }
}


const historyOfEverything = new Book('Bill Bryson', 'A Short History of Nearly Everything', 544);

console.log(historyOfEverything);
historyOfEverything.toggleCheckedOutStatus();
console.log(historyOfEverything._isCheckedOut);
historyOfEverything.addRating(4);
historyOfEverything.addRating(5);
historyOfEverything.addRating(5);
console.log(historyOfEverything.getAverageRating());

const hobbit = new Book('J.R. Tolkien', 'Hobbit', 744);

const speed = new Movie('Jan de Bont', 'Speed', 116);

console.log(speed);
speed.toggleCheckedOutStatus();
console.log(speed._isCheckedOut);
speed.addRating(1);
speed.addRating(1);
speed.addRating(5);
console.log(speed.getAverageRating());



const newCd = new Cd('Ocean', 'Kasaija Akiiki');


newCd.addSong('Billy Jean');
newCd.addSong('Mafia');
newCd.addSong('Yeke Yeke');

console.log(newCd);

const Test = new Catalog();
Test.mediaList = historyOfEverything;
Test.mediaList = speed;
Test.mediaList = hobbit;
Test.mediaList = newCd;
console.log(Test)
2 Likes

This works except when you console.log the Test all of the array items show “object” instead of the actual ratings or songs. Is there a way to get the arrays to show the actual data?

You could add methods to Catalog, depending on what exactly you want to display.

i would start by honoring javascript cammelcase naming convention, so it should be myCatalog. and test.

you should change the getter method from mediaList to myCatalog (given myCatalog is the property and not mediaList). mediaList is just a parameter, it only exist within the constructor method.

then we can do test.myCatalog, this will call the getter method, if coded correctly, you should be able to display output.

Thank you for the help. The code you’re looking at in this page is not mine.

I’m good on the camelCase so I’ll go ahead and work with your ideas. Can I post my code on the forum page after I get it fixed or if I have further issues?

sure, but preferable in a separate topic.

You can press the share button, and then click new topic, this will create a linked topic (so the topics are “linked” but are two separated topics)

remember that getters and setters manipulate properties of classes, so it should be:

get myCatalog() {

}

mediaList is a parameter, it just existing within the constructor method

1 Like

I read it that way too. After reviewing this thread, I think the purpose of step 25 bullet 5 is to just point out that this is why we create subclasses within classes.

This is in reference to the original thread about adding a catalog to build a library.

The way I understand it, a class is a function, and a catalogue should be a list of objects we can refer to. An object is not made until the class function is called with “new”. So I propose the following solution: make a catalog object which contains books, movies and CDs. Each time we add a book, movie or CD to a catalog, it will create the object with the appropriate class. Then we can add media, remove media, list everything we have or look up something to do actions on them like add ratings or toggle check out statues.

Here is the code. The catalog part starts on line 80, after the class definitions. Feedback is welcome!

class Media {
    constructor(title) {
      this._title = title;
      this._isCheckedOut = false;
      this._ratings = [];
    }
        
      get title() {
        return this._title;
      }
    
      get isCheckedOut() {
        return this._isCheckedOut;
      }
      
      toggleCheckOutStatus() {
        this._isCheckedOut = !this._isCheckedOut;
      }
    
      addRating(rating) {
        this._ratings.push(rating);
      }
    
      getAverageRating(rating) {
        const reducer = (accumulator,currentValue) => accumulator + currentValue ;
        return this._ratings.reduce(reducer)/this._ratings.length;
      }
       
  }
  
  class Book extends Media {
    constructor(title,author,pages) {
      super(title)
      this._author = author;
      this._pages = pages;
    }
    
    get author() {
      return this._author;
    }
    
    get pages() {
      return this._pages;
    }
    
  }
    
  class Movie extends Media {
    constructor(title,director,runTime,movieCast) {
      super(title);
      this._director = director;
      this._runTime = runTime;
      this._movieCast = [];
    }
    
    get director() {
      return this._director;
    }
    get runTime() {
      return this._runTime;
    }  
  }
  
  class CD extends Media {
    constructor(title,artist,songs) {
      super(title);
      this._artist = artist;
      this._songs = songs;
    }
    
    get artist() {
      return this._artist;
    }
    
    get songs() {
      return this._songs;
    }
  }

  const catalog = {
    _books: [],
    _movies: [],
    _CDs: [],
    get books() {
      return this._books;
    },
    get movies() {
      return this._movies;
    },
    get CDs() {
      return this._CDs;
    },
    addBookToCatalog (title,author,pages) {
      book = new Book(title,author,pages);
      this._books.push(book);
      return book;
    },
    removeBookFromCatalog (title,author) {
      const books = this.books;
      let bookToRemove = books.findIndex(function(book) {
         return book.title === title && book.author === author;
      });
      this._books.splice(bookToRemove);
   },
    getBookFromCatalog (title,author) {
      const books = this.books;
      let findBook = books.find(function(book) {
        return book.title === title && book.author === author;
      })
      return findBook;
    },
    addMovieToCatalog (title,director,runTime) {
      const movie = new Movie(title,director,runTime);
      this._movies.push(movie);
    },
    removeMovieFromCatalog (title,director) {
      const movies = this.movies;
      let movieToRemove = movies.findIndex(function(movie) {
         return movie.title === title && movie.director === director;
      });
      this._movies.splice(movieToRemove);
   },
    getMovieFromCatalog (title,director) {
      const movies = this.movies;
      let findMovie = movies.find(function(movie) {
        return movie.title === title && movie.director === director;
      })
      return findMovie;
    },
    addCDToCatalog (title,artist,songs) {
      const cd = new CD(title,artist,songs);
      this._CDs.push(cd);
    },
    removeCDFromCatalog (title,artist) {
      const cds = this.cds;
      let bookToRemove = cds.findIndex(function(cd) {
         return cd.title === title && cd.artist === artist;
      });
      this._books.splice(bookToRemove);
   },
    getCDFromCatalog (title,artist) {
      const cds = this.cds;
      let findCD = cds.find(function(cd) {
        return cd.title === title && cd.artist  === artist;
      })
      return findCD;
    },
    listCatalog () {
      books = this.books;
      movies = this.movies;
      cds = this.cds;
      if (books) {books.forEach(function(book) {console.log(`Book Title: ${book.title}, Author: ${book.author}, 
      Checked out: ${book.toggleCheckOutStatus()}, Average rating: ${book.getAverageRating()}`)})};
      if (movies) {movies.forEach(function(movie) {console.log(`Movie Title: ${movie.title}, Director: ${movie.director}, 
      Checked out: ${movie.toggleCheckOutStatus()}, Average rating: ${movie.getAverageRating()}`)})};
      if (cds) {cds.forEach(function(cd) {console.log(`Book Title: ${cd.title}, Artist: ${cd.artist}, 
      Checked out: ${cd.toggleCheckOutStatus()}, Average rating: ${cd.getAverageRating()}`)})};
    }
  }
  
  catalog.addBookToCatalog('A Short History of Nearly Everything','Bill Bryson',544);
  historyOfEverything = catalog.getBookFromCatalog('A Short History of Nearly Everything','Bill Bryson');
  console.log(historyOfEverything.isCheckedOut);
  historyOfEverything.toggleCheckOutStatus();
  console.log(historyOfEverything.isCheckedOut);
  historyOfEverything.addRating(4);
  historyOfEverything.addRating(5);
  historyOfEverything.addRating(5);
  console.log(historyOfEverything.getAverageRating());
  
  catalog.addMovieToCatalog('Speed','Jan de Bont',116);
  speed = catalog.getMovieFromCatalog('Speed','Jan de Bont');
  speed.toggleCheckOutStatus();
  console.log(speed.isCheckedOut);
  speed.addRating(1);
  speed.addRating(1);
  speed.addRating(5);
  console.log(speed.getAverageRating());

  catalog.listCatalog();
5 Likes

Hey everyone,

I am trying to add the Catalog class to the library, and this is what I’ve come up with:

class Catalog {
  constructor(type){
    this.type = type;
  }
  get type() {
    return this._type;
  }
};
class Media extends Catalog {
  constructor(type, title, isCheckedOut, ratings){
    super(type);
    this._title = title;
    this._isCheckedOut = false;
    this._ratings = [];
  }
  get title(){
    return this._title;
  }
  get isCheckedOut() {
    return this._isCheckedOut;
  }
  set isCheckedOut(status){
    this._isCheckedOut = status;
  }
  get ratings() {
    return this._ratings;
  }
  
  toggleCheckOutStatus() {
    this.isCheckedOut = !this.isCheckedOut;
  }
  getAverageRating() {
    let ratingSum = this.ratings.reduce((currentSum, rating)  => currentSum + rating, 0);
    const ratingsLength = this.ratings.length;
    const avgRating = ratingSum / (ratingsLength);
    return avgRating.toFixed(1);
    
    }
                 
  
  addRating(rating) {
    if(rating >= 1 && rating <= 5){
    this.ratings.push(rating);
  } else {
    console.log(`Rating must be a number ranging from 1 to 5`);
  }
  }
};
class Book extends Media {
  constructor(title, author, pages){
    super(title);
    this._author = author;
    this._pages = pages;
  }
  get author() {
    return this._author;
  }
  get pages() {
    return this._pages
  }
};
class Movie extends Media {
  constructor(title, director, runTime){
    super(title);
    this._director = director;
    this._runTime = runTime;
  }
  get director() {
    return this._director;
  }
  get runTime() {
    return this._runTime;
  }
};
class CD extends Media {
  constructor(title, artist, songs){
    super(title);
    this._artist = artist;
    this._songs = songs;
  }
  get artist() {
    return this._artist;
  }
  get songs() {
    return this._songs;
  }
  set songs(song) {
    this._songs.push(song);
    }
  shuffle(){
    let t = this._songs.slice();
    let i, j, k = t.length; 
    for (i = k; i > 0; i--){
      let j = Math.floor(Math.random() * k);
      [t[i-1], t[j]] = [t[j], t[i-1]];
    }
    return t;
  }
};
const historyOfEverything = new Book('A Short History of Nearly Everything', 'Bill Bryson', 544);
historyOfEverything.toggleCheckOutStatus();
//console.log(historyOfEverything.isCheckedOut);
historyOfEverything.addRating(4);
historyOfEverything.addRating(5);
historyOfEverything.addRating(5);
//console.log(historyOfEverything.getAverageRating());
const speed = new Movie('Speed', 'Jan de Bont', 166);
speed.toggleCheckOutStatus();
//console.log(speed.isCheckedOut);
speed.addRating(1);
speed.addRating(4);
speed.addRating(5);
//console.log(speed.getAverageRating());
const queenInnuendo = new CD('Innuendo', 'Queen', ['Innuendo', "I'm Going Slightly Mad", 'Headlong', "I Can't Live with You", "Don't try So Hard", 'Ride the Wild Wing', "All God's People", 'These Are the Days or Our Lives', 'Delilah', 'The Hitman', 'Bijou']);
//console.log(queenInnuendo);
queenInnuendo.songs = 'The Show Must Go On';
//console.log(queenInnuendo);
//console.log(queenInnuendo.shuffle());

I have ran this code on two environments (Codecademy and playcode) and I am not getting any error messages. Can you take a look and see if I have done it correctly? The Catalog class is at the very top of the code, by the way. Thanks!

1 Like

I think I’ve managed to do it:

class Catalog {
  constructor() {
    this._books = [];
    this._movies = [];
    this._cds = [];
  }
}

const fullCatalog = new Catalog

Then within each subclass constructor I add this extra step (e.g. Movies):

class Movie extends Media {
  constructor(director, title, runTime) {
    super(title);
    this._director = director;
    this._runTime = `${runTime} mins`;

    fullCatalog._movies.push({[this._title]: this._director }); // this step pushes the info to the catalog instance
  }

This is how I’ve interpreted the objective – now when I log ‘fullCatalog’ to console I get a constantly updating catalog.

I’m sure the catalog can be parsed and used to do other things (search etc.) in a real world situation…?

1 Like

Not sure that creating a dependency on an instance of another class is a good idea.

class Catalog {
  constructor() {
    this._books = [];
    this._movies = [];
    this._cds = [];
  }
  set movies(movie) {
    this._movies.push({
      'title': movie.title,
      'director': movie.director,
      'runtime': movie.runTime
    });
  }
  // ...
}
const location = new Catalog();
location.movies = new Movie('El Camino', 'Vince Gilligan', 122);

Hope my line of thinking is clear. There are missing pieces here that are just assumed.

Just throwing this out there…

console.log(location.movies.find(x => x.title === 'El Camino'))
2 Likes

ahhhhh right this makes sense, great to know

1 Like

Hi, interesting solution. I tried it and it works:)
the problem is that I don’t understand it.
How are able to use the setter with myCatalog.mediaList = historyOfEverything;?
You are not providing any parameter for the setter function?
Thankful for answers!

here:

 myCatalog.mediaList = historyOfEverything;

mediaList is the setter, because _mediaList is the property.

historyOfEverything is the argument. Bit of magic going on here (or i should say, JS does a lot of things under the hood here)