Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript Practice Object Interaction Checking Out and Returning a Book Solution: Charging Fines to Patrons

Muhammad Umar
Muhammad Umar
7,817 Points

Help: chargeFines() What would happen if this method was called more than once while a patron had the same book overdue?

Hi there,

I am trying to implement the improvement in the chargeFines()method, mentioned at the end of the solution for this challenge, where the Patron does not get charged again when the method is run.

Here is snapshot of my TreeHouse Workspace https://w.trhou.se/jrx29d56bc

Consider: What would happen if this method was called more than once while a patron had the same book overdue? How might you improve this method? What type of fine system would you design? Share your thoughts in the Treehouse community!

My thought process is this

  • Keep track of the Patrons that have already been charged. Maybe in an lateChargedPatron array.
  • If the Patron exists in the lateChargedPatron array, then find the difference of the days from the last time Patron was charged and then add that to their balance. Example: If it has been 1 day since the last time we ran the chargeFines() method, add 10 cents to Johnny balance.
  • If not, then run the method like we did in the exercise, to charge by calculating the difference between the current - dueDate * the balance

The issue is that when I iterate through each object in the latePatrons array then I don't have a way to iterating each object in the lateChargedPatron array within the same loop to compared the properties and see if the Patron is included or not. Would really appreciate some guidance on how to go out approaching this problem. I am still in the JavaScript learning phase. I tried a few things but didn't make much progress.

chargeFines() method

    chargeFines() {
        const today = new Date();
        const latePatrons = this.patrons.filter(patron => patron.currentBook !== null && patron.currentBook.dueDate < today);

        // Challenge: run chargesfine() method without doubling the fine for the patron

        // Array to keep track of Patrons that have already been charged
        const lateChragedPatron = [];

        // Using for .. of loop to iterate over array objects. Not using
        // for..in since that iterated over properties

        for (const latePatron of latePatrons) {

            // If patron is already charged, charge him only for the difference of days since last check
            if (lateChragedPatron.includes(latePatron.name)) {
                console.log("Dont Charge")
                // still working on this
            } else {
                const daysLate = today.getDate() - latePatron.currentBook.dueDate.getDate();
                latePatron.balance += (daysLate * this.dailyFine);
                lateChragedPatron.push(latePatron.name);              
            }

            console.log(lateChragedPatron);
        }

This is the output I get when I invoke the method a few times from my app.js file . It seems that the Patron is not getting pushed to the lateChargedPatron array. I am guessing that the array get empty every time the method is invoked?

app.js

$(document).ready(function () {
    const library = new Library();
    const book = new Book('Harry Potter and the Sorcerer\'s Stone', 'J.K. Rowling', '978-0439708180');
    const patron = new Patron('Johnny Bravo', 'awesome@awesome.com');

    library.addBook(book);
    library.addPatron(patron);

    patron.checkout(book);

    console.log(patron);
    console.log(library.chargeFines());
    console.log(library.chargeFines());
    console.log(library.chargeFines());
});

OUTPUT

Patron { 
balance: 4.2
​currentBook: Object { title: "Harry Potter and the Sorcerer's Stone", author: "J.K. Rowling", isbn: "978-0439708180", … }
​email: "awesome@awesome.com"
​name: "Johnny Bravo" }

Array [ "Johnny Bravo" ]

Array [ "Johnny Bravo" ]

Array [ "Johnny Bravo" ]

Your help is much appreciated!

4 Answers

Steven Parker
Steven Parker
229,644 Points

I noticed the spelling of "lateChragedPatron". It's probably OK if it's used consistently, but it looks funny. And yes, it gets created as an empty array at the beginning of the method but then is rebuilt. The output shown indicates that "Johnny Bravo" was added each time, so I'm not sure what the issue is.

Also, I may not be remembering this exercise correctly, but does this library limit one check-out per patron? If not, it seems the original fines system only charges fines on a patron's "currentBook" (last checked out). But what if they have multiple books out and overdue? Shouldn't each get a fine? So I was thinking instead of "latePatrons" it might make more sense to identify "lateBooks". Similarly, the check-in method should take a specific book argument.

For a large project like this, it may help to share your complete environment to make it easier to replicate and analyze issues. For that, make a snapshot of your workspace and post the link to it here.

Muhammad Umar
Muhammad Umar
7,817 Points

Hi Steven Parker, thanks so much taking the time to answer. I just noticed the spelling myself and yes, it looks weird :) I will edit that and make it to something else. You might have glanced over it but I do have my workspace screenshot at the beginning of my previous message. I will paste it again here Here is snapshot of my TreeHouse Workspace https://w.trhou.se/jrx29d56bc

Ah! So I was under the impression that the first time the loop in the code runs, it will add "Johnny Bravo" name property into the empty lateChargedPatron array and then the second time I invoke the chargeFines() method, it should see that "Johnny Bravo" is already in the lateChargedPatron array and print Don't Charge to the console.

Are you saying that each time I will invoke the chargeFines() method the lateChargedPatron will be given an empty array and whatever was in it from before gets overwritten? Will this have to be done by some sort of database where its keeping track of all the Patrons that were charged late fees.

Yes, you are correct, this exercise was limited to checking out only one book. I love the "lateBooks" idea and will try to implement that next.

My goal with this one was to keep track of the Patrons who were already charged a late fee and lets say I invoke the chargeFines() method on the same day, that particular Patron should not get charged again.

The future goal is to add a logic where I invoke the method a day later and it adds 10c to the current balance. I am guessing that I will have to create a new Date() object which will keep track of when the Patron was last charged. The difference of the current date and the last charged date will give me the number of additional days the fees needs to be charged.

So first time around the balance for Johnny Bravo is 1.40000... If I invoke the chargeFines() method again it should output "Dont charge" and maintain the 1.40000... balance.

Right now it adds "Johnny Bravo" every iteration and the balance keeps getting doubled.

Hope this explanation helps.

Again, I really appreciate your help.

    chargeFines() {
        const today = new Date();
        const latePatrons = this.patrons.filter(patron => patron.currentBook !== null && patron.currentBook.dueDate < today);

        // Challenge: run chargeFines() method without doubling the fine for the patron
        const lateChargedPatron = [];

        for (const latePatron of latePatrons) {
            // If patron is already charged, charge him only for the difference of days since last check
            if (lateChargedPatron.includes(latePatron.name)) {
                console.log("Dont Charge")
            } else {
                const daysLate = today.getDate() - latePatron.currentBook.dueDate.getDate();
                latePatron.balance += (daysLate * this.dailyFine);
                lateChargedPatron.push(latePatron.name);              
            }

            console.log(lateChargedPatron);
        }
Steven Parker
Steven Parker
229,644 Points

If you added a "lastCharged" field to the Patron, you could put the current date in it when they are charged a fee.

Then, when computing the fee again, any patron with that value set to the current date could be skipped. Otherwise, the additional fee could be computed from that date instead of the book's due date.

Muhammad Umar
Muhammad Umar
7,817 Points

Thanks for the pointers Steven Parker. That's a great approach and I can also use the same to remove Patrons who have paid the balance.

I was able to find a solution to my "keeping track of the overdue Patrons" by adding an overdue array to the Library object. Now it doesn't print "Johnny Bravo" if I invoke the chargeFines() method multiple times. Now I will use the Date approach you mentioned and that should make it much simpler.

class Library {
    constructor(books, patrons, dailyFine, overdue) {
        this.books = [];
        this.patrons = [];
        this.dailyFine = 0.1;
        this.overdue = []; // Store Patrons who have overdue books
    }

Now when I invoke the chargeFines() method the second of third time, it knows Patron.name exists in the overdue array. It was a simple thing but I took me a while to make that connection

    chargeFines() {
        const today = new Date();
        const latePatrons = this.patrons.filter(patron => patron.currentBook !== null && patron.currentBook.dueDate < today);

        for (const latePatron of latePatrons) {
            if (this.overdue.includes(latePatron.name)) {
                console.log("Dont Charge")
            } else {
                const daysLate = today.getDate() - latePatron.currentBook.dueDate.getDate();
                latePatron.balance += (daysLate * this.dailyFine);
                this.overdue.push(latePatron.name);              
            }
        }

Thanks again for all your time and help. I really appreciate it. This was a very good exercise by TreeHouse and I hope to see a lot more like these. Hopefully this will be helpful for someone else.

Doesn't this only work if "today" and the "due date" fall on the same month?? wouldn't it be better to use some sort of a function that gets executed automatically, say every 24 hours and adds the daily fine to the balance of the over due patrons.

Steven Parker
Steven Parker
229,644 Points

Date objects uniquely identify the day, month, and year, so there would be no requirement for the days to fall in the same month.

but if "today" is 26th of December and "dueDate" was 28th of November then "daysLate = today.getDate() - dueDate.getDate()" will be equal to -2 right ?

Here is the console image

Steven Parker
Steven Parker
229,644 Points

The original question was about something else, but yes, since "getDate" extracts just the day of the month from the date, doing math on these values isn't really what you want here.

You'd probably want to use "getTime" instead, and divide the difference by the number of milliseconds in a day (1000*60*60*24). This would give you total number of days.