Start learning free

LESSON 560 OF 574 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Ticket Numbers in Swift: A Shared static 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 7
    Ctrl + Enter to check

    SWIFT · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Ticket Numbers in Swift, explained

    Problem overview

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

    The class has three parts:

    • Ticket(name) adds 1 to the counter and gives the new ticket that value as its 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.

    Creating tickets for Ann, Bob and Cy gives them numbers 1, 2 and 3, and issued() returns 3. The lesson's tests print each ticket as its name, a space, # and its number, then the value of issued() on its own line.

    Approach

    There are two kinds of data here, and they live in different places:

    • the counter is one value for the whole class, so it is declared static and is reached through the type name, Ticket.count;
    • the name and number differ from ticket to ticket, so they are ordinary properties, one pair per object.

    The important detail is the order inside init: first move the counter forward, then copy its value into the ticket. The copy stays put when the counter moves on, which is why every ticket must store its own number. Everything is O(1).

    Step by step

    1. Declare the counter: static var count = 0 at the top of the class. The static keyword makes it belong to the type.
    2. Declare the per-ticket properties: let name: String and let number: Int, without static. They are let because a ticket never changes once it exists.
    3. Store the name in init: self.name = name.
    4. Add 1 to the shared counter: Ticket.count += 1. Every ticket counts once, whatever its name.
    5. Copy the counter into the ticket: self.number = Ticket.count. Right after a reset the first ticket finds the counter at 1, so numbering starts at 1.
    6. issued() is a static func that returns Ticket.count. The tests call it on the type, with no ticket at all.
    7. resetCounter() is a static func that assigns 0 to Ticket.count.

    The solution in Swift

    class Ticket {
        static var count = 0
        let name: String
        let number: Int
    
        init(_ name: String) {
            self.name = name
            Ticket.count += 1
            self.number = Ticket.count
        }
    
        static func issued() -> Int {
            return Ticket.count
        }
    
        static func resetCounter() {
            Ticket.count = 0
        }
    }
    

    Using it:

    Ticket.resetCounter()
    var tickets: [Ticket] = []
    for n in ["Ann", "Bob", "Cy"] { tickets.append(Ticket(n)) }
    for t in tickets { print("\(t.name) #\(t.number)") }
    print(Ticket.issued())
    Ticket.resetCounter()
    print(Ticket.issued())
    let m = Ticket("Mia")
    print(m.name, m.number)
    
    Ann #1
    Bob #2
    Cy #3
    3
    0
    Mia 1
    

    After the reset, issued() is 0 and the next ticket is number 1 again.

    Swift details worth knowing

    • Static members are reached through the type name. Inside init you write Ticket.count, not plain count, and the tests call Ticket.issued(). An object has no issued of its own.
    • init(_ name: String) has an underscore, so a call is Ticket("Ann") without a label.
    • A let property must be set exactly once in init. self.name and self.number each get one assignment, and nothing can change them afterwards.
    • Two names for two kinds of data. The static counter is called count and the per-ticket value number, so reading the code never mixes them up.

    Edge cases and common mistakes

    • Reading the counter later instead of storing the number. If the tests print Ticket.count for each ticket after all were created, every line shows the last number. With the three tickets above it prints Ann #3, Bob #3, Cy #3. Storing number in each ticket avoids this.
    • No tickets at all. After resetCounter() and no new ticket, issued() returns 0.
    • Equal names. Two tickets named Mia get different numbers, because the number comes from the counter and not from the name.
    • Copying before incrementing. If self.number = Ticket.count comes before Ticket.count += 1, the numbers start at 0: Ann, Bob and Cy get 0, 1 and 2 while issued() returns 3.
    • Forgetting the reset. The counter lives as long as the program, so without resetCounter() a second group of tickets would continue from where the first one stopped.

    Complexity

    Creating a ticket, issued() and resetCounter() each do a constant number of steps, so each is O(1) time. The class stores one shared integer plus a name and a number per ticket, so a test with n tickets uses O(n) space, which is just the tickets themselves.

    Static or per-object?

    A quick rule decides where each piece of data goes. Ask: "if I make a thousand objects, should there be a thousand copies of this, or one?" The name and the number differ per ticket, so there are a thousand copies. The count of tickets made so far is the same fact for everybody, so there is one.

    • Three tickets, Ann, Bob and Cy, after a reset: numbers 1, 2, 3 and issued() is 3.
    • Two tickets both named Mia: numbers 1 and 2, issued() is 2.
    • No tickets after a reset: issued() is 0.
    Keep going with Swift500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free