rubyguides

Array#pop

arr.pop(n=nil) -> obj or array

Ruby’s collection endpoints are the basic building blocks for stack and queue data structures. pop and push operate on the right side, shift and unshift on the left. All four mutate the structure in place, which matters when you’re chaining methods or passing references around.

These methods are small, but they describe the direction of your data very clearly. When you want to add or remove from either end, the standard library gives you direct tools instead of requiring a custom wrapper.

That directness is one reason these methods show up in everyday Ruby code. They make the flow of values visible, which helps the reader understand whether the structure is acting like a stack, a queue, or just a simple ordered list.

syntax

arr.pop        # remove and return last element
arr.pop(n)     # remove and return last n elements as array
arr.push(*obj)  # append objects to end, return arr
arr.shift       # remove and return first element
arr.shift(n)    # remove and return first n elements as array
arr.unshift(*obj)  # prepend objects to start, return arr

parameters

pop and shift

ParameterTypeDefaultDescription
nIntegernilWhen provided, removes and returns n elements as an array instead of a single element

push and unshift

ParameterTypeDefaultDescription
*objectsObjectRequiredOne or more objects to add to the collection

examples

basic remove operations

stack = [1, 2, 3, 4, 5]

# pop removes from the end
stack.pop
# => 5
stack
# => [1, 2, 3, 4]

# shift removes from the beginning
stack.shift
# => 1
stack
# => [2, 3, 4]

The separation between ends matters when you model real-world behavior. A stack grows and shrinks from the same side, while a queue accepts entries on one end and releases them from the other. Knowing which end of the collection you are working with helps you pick between pop and shift or between push and unshift without second-guessing.

basic add operations

queue = [2, 3]

# push adds to the end
queue.push(4)
queue
# => [2, 3, 4]

# unshift adds to the beginning
queue.unshift(1)
queue
# => [1, 2, 3, 4]

Ruby lets you pull or push several entries at once by passing an integer argument to pop or shift. This is useful when you need to batch-process items or drain a fixed number of entries from the list without writing a loop. The returned value is always a collection, even when you request just one element using the n form.

removing multiple elements

letters = ['a', 'b', 'c', 'd', 'e']

# pop with n returns array of removed elements
letters.pop(2)
# => ["d", "e"]
letters
# => ["a", "b", "c"]

# shift with n
letters.shift(2)
# => ["a", "b"]
letters
# => ["c"]

Just as you can remove several items at once, you can also add several items in a single call. push and unshift accept a variable number of arguments, so you can prepend or append a whole series of values in one statement. This keeps your code compact when you already hold the values you want to insert.

adding multiple elements

numbers = [3]

# push multiple at once
numbers.push(4, 5, 6)
numbers
# => [3, 4, 5, 6]

# unshift multiple at once
numbers.unshift(1, 2)
numbers
# => [1, 2, 3, 4, 5, 6]

The return value of these methods reveals an important design choice in Ruby’s API. pop and shift hand back the removed elements so you can use them right away. push and unshift return the modified structure itself, which supports method chaining but can be surprising if you expect the new length or the added items instead of the collection.

return value differences

arr = [1, 2, 3]

# pop and shift return the element(s) removed
result = arr.pop
result
# => 3

# push and unshift return the modified array
result = arr.push(4)
result
# => [1, 2, 4]

result = arr.unshift(0)
result
# => [0, 1, 2, 4]

These four methods cover the basic patterns for ordered collections. Beyond simple removal and insertion, Ruby programmers use these primitives to build higher-level abstractions like stacks and queues without any extra classes or libraries. The next section shows how a few lines of push and pop create a complete LIFO data structure.

common patterns

stack (LIFO)

# Stack: Last In, First Out
stack = []

stack.push(1)
stack.push(2)
stack.push(3)

stack.pop  # => 3
stack.pop  # => 2
stack.pop  # => 1
stack.pop  # => nil

A stack processes the most recently added item first, which is the natural order for undo systems, expression evaluation, and backtracking algorithms. A queue preserves insertion order and processes the oldest item first, making it the right choice for job scheduling, breadth-first traversal, and event handling. The choice between pop and shift determines which access pattern you get from the same underlying list.

queue (FIFO)

# Queue: First In, First Out
queue = []

queue.push("first")
queue.push("second")
queue.push("third")

queue.shift  # => "first"
queue.shift  # => "second"
queue.shift  # => "third"
queue.shift  # => nil

When you combine shift with a while loop, you create a simple consumption pattern that drains the collection as it processes each entry. This is more memory-efficient than iterating and clearing separately, because the list shrinks as you go and Ruby can reclaim the space behind the scenes. Just keep in mind that this approach destroys the original data, so copy it first if you need to preserve the contents.

processing with destructive methods

# Process items while removing them
items = ["a", "b", "c", "d", "e"]

while (item = items.shift)
  puts "Processing: #{item}"
end

items
# => []

You can also build a structure outward from a center point. Start with the middle values, then use unshift to extend leftward and push to extend rightward. This pattern is handy when you are constructing a sequence where the order depends on conditions that are not known in advance.

building arrays from both ends

centered = [3, 4]

centered.unshift(2)
centered.unshift(1)
centered.push(5)
centered.push(6)

centered
# => [1, 2, 3, 4, 5, 6]

Edge cases reveal the real behavior of these methods. When a list is empty or nearly empty, pop and shift have well-defined return values that can surprise programmers coming from languages where similar operations throw exceptions. Understanding these boundary behaviors saves debugging time later.

edge cases

empty arrays

empty = []

empty.pop      # => nil
empty.shift     # => nil
empty.pop(2)    # => []
empty.shift(2)  # => []

Ruby handles out-of-bounds requests gracefully. When you ask for more elements than the collection contains, you get back everything that is available rather than an error. This lenient behavior prevents common off-by-one bugs but means you should check the return size when the exact count matters in downstream logic.

n larger than array size

small = [1, 2]

small.pop(10)    # => [1, 2]
small.shift(10)  # => [1, 2]
# Both return all elements, array becomes empty

One subtlety worth remembering is that pop and shift return nil for an empty collection, but nil is also a valid element you can store in a Ruby list. If you need to distinguish between “the collection was empty” and “the element itself is nil,” check the length before removing, or use empty? to guard the call. The same applies to false, which is also a legitimate value to push onto a structure.

adding nil or false

arr = [1]
arr.push(nil, false, 2)
arr
# => [1, nil, false, 2]

using end operations well

pop, push, shift, and unshift are small methods, but they shape how the whole collection behaves. They are the right tools when you want to treat it like a stack or queue and you are fine with mutation. Because they change the original structure, the code around them should make that side effect obvious. Use the n forms when you need to move several items at once, and remember that empty arrays return nil or an empty collection depending on which form you use. That difference often matters in loop logic.

These methods also make it easy to express the direction of your data flow. When items move in from one end and leave from the other, the code reads like the structure it models. That clarity is one reason the same list can act like a queue in one method and a stack in another. If mutation would be surprising, though, a copied structure or a separate data structure may be easier to reason about.

See Also