We use cookies (including Google cookies) to personalize ads and analyze traffic. By continuing to use our site, you accept our Privacy Policy.

Event Emitter

Number: 2797

Difficulty: Medium

Paid? No

Companies: Tinkoff, Yandex


Problem Description

Design an EventEmitter class that supports subscribing to events and emitting them. For any given event, multiple listeners (callbacks) can subscribe. When an event is emitted, all subscribed callbacks are executed in the order they were added and their results are returned in an array. Additionally, each subscription returns an object with an unsubscribe method that removes that particular callback from future emissions.


Key Insights

  • Use a data structure (e.g., a dictionary/hashmap) to map event names to a list of callback functions.
  • Subscribing to an event appends the callback to the corresponding list.
  • Emitting an event involves iterating over the list of callbacks in order and invoking each one with the provided arguments.
  • The unsubscribe functionality should remove the callback so that it is not invoked on subsequent event emissions.
  • When unsubscribing, care must be taken to remove only the specific subscription while preserving the order of other callbacks.

Space and Time Complexity

Time Complexity:

  • subscribe: O(1) on average to add a callback.
  • emit: O(n) where n is the number of callbacks subscribed to the event.
  • unsubscribe: O(n) in the worst case to locate and remove the callback.

Space Complexity:

  • O(n) for storing the subscriptions, where n is the total number of subscriptions.

Solution

We implement the EventEmitter class by maintaining a mapping (e.g., a dictionary in Python or an object in JavaScript) from event names to lists of callbacks. The subscribe method adds the callback to the list for the event and returns an unsubscribe method that, when called, removes that callback. The emit method looks up the event name, and if present, iterates through its callbacks, calling each with the provided arguments and collecting the results.

Key details include:

  • Keeping the order of callbacks intact for execution.
  • Ensuring that unsubscription correctly removes the selected callback.
  • Handling optional arguments for the emit method.

Code Solutions

class EventEmitter:
    def __init__(self):
        # Dictionary mapping event name to list of callbacks
        self.events = {}
        
    def subscribe(self, event, callback):
        # If the event does not exist yet, initialize with an empty list
        if event not in self.events:
            self.events[event] = []
        # Append the callback to the event's callback list
        self.events[event].append(callback)
        
        # Define the unsubscribe function which removes the callback
        def unsubscribe():
            if event in self.events:
                try:
                    self.events[event].remove(callback)
                except ValueError:
                    pass  # Callback already removed
                
        # Return an object with the unsubscribe method
        return type("Subscription", (), {"unsubscribe": lambda self: unsubscribe()})()
    
    def emit(self, event, args=None):
        # If no arguments provided, use an empty list
        if args is None:
            args = []
        # Retrieve all callbacks for the event, or return empty list if event not present
        results = []
        if event in self.events:
            for cb in self.events[event]:
                results.append(cb(*args))
        return results

# Example usage:
# emitter = EventEmitter()
# sub = emitter.subscribe("firstEvent", lambda *args: ",".join(map(str, args)))
# print(emitter.emit("firstEvent", [1,2,3]))
# sub.unsubscribe()
# print(emitter.emit("firstEvent", [4,5,6]))
← Back to All Questions