Skip to Content
Course content

40: Implementing a Stack and Queue

Click on the "Edit" button in the top corner of the screen to edit your slide content.

By now, you're comfortable with Arrays, but in the real world, we often need to restrict how we interact with data to ensure our logic stays predictable. That's where Stacks and Queues come in. They aren't "new" data structures in Ruby so much as they are specific ways of using an Array.

Wait, can't I just use an Array instead of building a Stack class?

Technically, yes. Ruby's Array class already has push and pop, which are the two primary operations of a Stack. If you just need to store a few things and pop them off, an Array is fine. But here is why I usually wrap it in a class: intentionality.

If you pass a raw Array to another developer, they might use insert or delete_at, which completely destroys the "Last-In, First-Out" (LIFO) guarantee of a stack. By wrapping it, you're telling the rest of your app, "You are only allowed to add to the top and take from the top."

class Stack
  def initialize
    @storage = []
  end

  def push(element)
    @storage << element
  end

  def pop
    @storage.pop
  end

  def peek
    @storage.last
  end

  def empty?
    @storage.empty?
  end
end

# Think of this like a browser's back button history
history = Stack.new
history.push("google.com")
history.push("github.com")
history.push("stackoverflow.com")

puts history.pop # => "stackoverflow.com" (The last page you visited)

How is a Queue different, and does Ruby handle it efficiently?

While a Stack is LIFO, a Queue is FIFO: First-In, First-Out. Think of it like a line at a coffee shop. The first person to get in line is the first person to get their latte. To do this in Ruby, we push to the end of the array but shift from the front.

I've had students ask if shift is slow because it has to move every other element in the array over one spot. In many languages, that's a huge performance hit. In Ruby, the internal implementation of Array is actually quite optimized for this, so for most applications, using shift is perfectly acceptable.

class Queue
  def initialize
    @storage = []
  end

  def enqueue(element)
    @storage << element
  end

  def dequeue
    @storage.shift
  end

  def front
    @storage.first
  end
end

# Example: A print queue
printer = Queue.new
printer.enqueue("Tax_Return.pdf")
printer.enqueue("Vacation_Photo.jpg")
printer.enqueue("Resume.pdf")

puts printer.dequeue # => "Tax_Return.pdf" (The first document sent)

When would I actually use these in a real project?

You'll run into these more often than you think. I once worked on a system that processed background emails. If we just used a list, we might accidentally process the newest email first, leaving the oldest ones sitting there for hours. A Queue was the only way to ensure fairness.

Stacks are the gold standard for any "Undo" feature. Every time the user performs an action, you push that action onto a stack. When they hit Ctrl+Z, you pop the most recent action and reverse it. If you used a Queue there, you'd be undoing the first thing the user did ten minutes ago, which would be a nightmare for the UX.




📋 Practical Task

Build a Customer Support Ticket Processor

You need to build a system that manages incoming support tickets. Tickets must be handled in the exact order they arrive to ensure fairness to the customers.

  • Create a TicketQueue class that wraps an array.
  • Implement an add_ticket(customer_name) method.
  • Implement a process_ticket method that removes the oldest ticket from the queue and returns a string: "Now helping: [customer_name]".
  • If the queue is empty, process_ticket should return "No tickets to process!".

Test your implementation by adding three different customers and processing them one by one to verify that the first person added is the first person helped.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.