Start learning free

LESSON 559 OF 573 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Ticket Numbers in Scala: Companion Object Counter

Practice O(1) Read the full explanation

A ticket machine gives every new ticket the next number: the first ticket gets 1, the next one 2, and so on. The counter belongs to the class, not to any one ticket: it is a static member of Ticket, one copy shared by all the tickets. Each ticket keeps its own number, copied from the counter when the ticket is created.

Implement the class Ticket:

  • Ticket(name) creates a ticket for name. It adds 1 to the class-level counter and gives the new ticket that value as its number. The ticket keeps its name and its number in two fields, name and number.
  • The static method issued() returns how many tickets were created since the counter was last reset.
  • The static method resetCounter() sets the counter back to 0, so the next ticket gets number 1 again.

For each test case, the test code calls resetCounter() through the class name, then creates one ticket per name, in order. Once all the tickets of the case exist, it prints each ticket as its name, a space, # and its number, one ticket per line, and then the value of issued() on a line of its own.

The starter code spells these names the way your language does: reset_counter in Python, Issued and ResetCounter in C#. In Python the counter is a class attribute and the two methods are marked @staticmethod; in Kotlin and Scala the counter and the two methods live in the companion object, as in the Static Members lesson.

 

Example 1:

Input:
    Ticket("Ann")
    Ticket("Bob")
    Ticket("Cy")
Output:
Ann #1
Bob #2
Cy #3
3
Explanation: resetCounter() set the counter to 0. Each new ticket adds 1 to it and keeps the new value,
so the tickets get 1, 2 and 3, and issued() returns 3.

Example 2:

Input:
    Ticket("Mia")
    Ticket("Mia")
Output:
Mia #1
Mia #2
2
Explanation: The number comes from the shared counter, not from the name, so the two tickets for Mia
get different numbers. The numbers start at 1 again because resetCounter() ran before this case.

Example 3:

Input:
    (no tickets)
Output:
0
Explanation: resetCounter() set the counter to 0 and no ticket was created, so issued() returns 0.

 

Constraints:

  • A test case creates from 0 to 1000 tickets.
  • A name is one word of 1 to 20 English letters. Two tickets can have the same name.
  • The tickets are printed only after all of them were created, so every ticket must store its own number: reading the shared counter when printing would give every ticket the last number.
STEP 1 OF 5
    Ctrl + Enter to check

    SCALA · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Ticket Numbers in Scala, explained

    Problem overview

    A ticket machine gives each new ticket the next number: 1 for the first, 2 for the second, and so on. The counter belongs to the class as a whole, not to any one ticket, so all tickets share one copy. Each ticket keeps its own number, copied from the counter when the ticket is created.

    The class Ticket needs:

    • Ticket(name), which adds 1 to the shared counter and keeps the new value as the ticket's number, next to its name;
    • issued(), which returns how many tickets were created since the last reset;
    • resetCounter(), which sets the counter back to 0, so the next ticket is number 1 again.

    Creating tickets for Ann, Bob and Cy gives them numbers 1, 2 and 3, and issued() returns 3.

    Approach: the counter lives in the companion object

    Scala has no static keyword. Instead, a class can have a companion object: an object with the same name, in the same file. It exists once for the whole program, so it is the place for anything that is shared, such as the counter and the two methods that read and reset it. Code anywhere can call them on the name: Ticket.issued(), with no ticket needed.

    The ticket's own number must be a field of the ticket, copied from the counter at creation. If a ticket read the counter every time it was asked for its number, every ticket would show the latest number once more tickets existed.

    Step by step

    1. Declare the counter in the companion object: var count = 0. It is a var because it changes, and there is one copy for all tickets.
    2. Count the new ticket. The statements in a class body run every time an object is created, so Ticket.count += 1 there runs once per ticket.
    3. Keep the number. val number: Int = Ticket.count copies the counter's current value into this ticket. The counter moves on as more tickets are made, but the copy stays. Right after a reset, the first ticket sees 1 here.
    4. issued returns count.
    5. resetCounter assigns 0 to count.

    The solution in Scala

    class Ticket(val name: String) {
        Ticket.count += 1
        val number: Int = Ticket.count
    }
    
    object Ticket {
        var count = 0
    
        def issued(): Int = {
            return count
        }
    
        def resetCounter(): Unit = {
            count = 0
        }
    }
    

    Scala details:

    • The class and its companion object share a name and must be in the same file. The class reaches the counter as Ticket.count, and the object's methods use count directly.
    • The class body is the constructor. Besides declaring fields, it runs its statements for each new object. That is what makes Ticket.count += 1 happen once per ticket.
    • The order matters. The increment comes before val number, so the number is the already increased value.
    • name comes from the header. val name: String in the parentheses makes it a field, so t.name works.

    Using the class

    Ticket.resetCounter()
    val ts = List(new Ticket("Ann"), new Ticket("Bob"), new Ticket("Cy"))
    for (t <- ts) println(s"${t.name} #${t.number}")
    println(Ticket.issued())

    It prints:

    Ann #1
    Bob #2
    Cy #3
    3

    All three tickets exist before anything is printed, yet each one shows its own number. After another Ticket.resetCounter(), Ticket.issued() returns 0. Two tickets for the same name get different numbers: with new Ticket("Mia") twice after a reset, the numbers are 1 and 2 and issued() is 2, because the number comes from the counter, not from the name.

    Complexity

    Creating a ticket, issued() and resetCounter() each take O(1) time. Each ticket stores a name and a number.

    Common mistakes

    • Making the number a method that reads the counter. Then every ticket reports the latest value. Copy it into a field at creation.
    • Putting the counter in the class. A var inside class Ticket is a separate copy for every ticket, so each would count only itself and every number would be 1.
    • Taking the number before the increment. The first ticket would get 0.
    • Forgetting the reset. The counter would carry on from an earlier set of tickets instead of starting at 1.
    Keep going with Scala500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free