Array#pop
Overview
Removes and returns the last element of a collection. The array is modified in place — the removed element is no longer present after pop is called.
Basic Usage
stack = ["first", "second", "third"]
top = stack.pop
top
# => "third"
stack
# => ["first", "second"]
pop mutates the original collection. The returned element is removed from the collection permanently.
Since pop modifies the receiver in place, it does not create a new object — the collection itself shrinks by one element with each call. This characteristic makes it a natural choice for building stack-based algorithms where items are consumed one at a time without allocating extra memory for intermediate copies. The method is safe to call repeatedly until the list is empty.
Removing multiple elements
Pass a number argument to remove and return the last n elements as a new array:
queue = [1, 2, 3, 4, 5]
last_three = queue.pop(3)
last_three
# => [3, 4, 5]
queue
# => [1, 2]
When you pass a number to pop, it always returns an array — even if only one element is removed. The integer argument tells Ruby how many trailing items to extract in one call — a common idiom when batching the tail end of a sequence without running a loop over individual removals. Passing 0 as the argument returns an empty list while leaving the original untouched, which can be useful in conditional logic where the pop count is computed at runtime.
items = ["a", "b", "c"]
result = items.pop(1)
result
# => ["c"]
items
# => ["a", "b"]
Calling pop(n) where n exceeds the collection’s length simply returns all remaining elements rather than raising an error. This lenient behavior means you do not need to guard the call with a size check when you only care about getting whatever is available from the tail end.
pop on an empty array
Without an argument, pop on an empty collection returns nil:
empty = []
empty.pop
# => nil
With an argument, pop(n) on an empty array (or when n is greater than the array length) returns an empty array. Ruby does not raise an error or return nil in this situation — the method is designed to handle edge cases gracefully by returning whatever it can extract from the available elements at the end. This is consistent with the general Ruby principle of avoiding surprising exceptions for predictable boundary conditions.
[].pop(5)
# => []
If you need to distinguish between “no element” and “empty array” scenarios, check the collection size first. Calling pop without arguments on a non-empty list always gives you a single element back, so a nil return reliably signals emptiness without ambiguity in the calling code. This guard pattern is especially helpful in methods that must distinguish between popping nil values from a list and receiving nil because the list was exhausted.
def safe_pop(arr)
return nil if arr.empty?
arr.pop
end
safe_pop([])
# => nil
The safe-pop wrapper above enforces a nil-on-empty contract that is common in Ruby codebases where callers want to avoid surprise nil values from legitimate element removals. Rather than scattering empty-checks at every call site, the wrapper centralizes the boundary logic in one reusable place.
pop vs shift
pop removes from the end of the sequence. shift removes from the beginning:
queue = ["first", "second", "third"]
queue.pop
# => "third"
queue
# => ["first", "second"]
queue.shift
# => "first"
queue
# => ["second"]
Think of pop as the opposite of push, and shift as the opposite of unshift.
| Method | Removes from | Opposite of |
|---|---|---|
pop | End | push |
shift | Beginning | unshift |
Practical Examples
Stack simulation
A stack works on last-in, first-out (LIFO) principle. push adds to the top, pop removes from the top:
stack = []
stack.push("第一项") # push item
stack.push("第二项")
stack.push("第三项")
stack.pop # pop item
# => "第三项"
stack.pop
# => "第二项"
stack
# => ["第一项"]
Stack operations underpinned by pop follow the LIFO discipline naturally because Ruby lists provide efficient amortized O(1) removal from the rightmost position. No re-indexing of earlier elements takes place after removing the last entry, which keeps the operation fast regardless of collection size.
Processing queue in reverse
When you need to process items in reverse order without reversing the original list:
tasks = ["分析数据", "生成报告", "发送邮件", "清理日志"]
while tasks.any?
current = tasks.pop
puts "处理: #{current}"
end
tasks
# => []
The reverse-destruction pattern using pop in a while loop is memory-efficient because it consumes the input list in place rather than building a second data structure. After the loop finishes, the original collection is empty, making cleanup automatic without extra housekeeping code.
Removing recently added items
Track a history of actions and remove the last one when needed:
history = ["打开文件", "编辑内容", "保存文件"]
# Undo the last action
undone = history.pop
puts "撤销: #{undone}"
# => "撤销: 保存文件"
history
# => ["打开文件", "编辑内容"]
Undo functionality backed by pop relies on the fact that removing the last entry from a history list effectively forgets the most recent operation. This pattern appears in command-line tools, text editors, and any system that maintains an action log with rollback support — the list of past states shrinks naturally with each undo step.
Recursive directory traversal
Build a directory tree by popping folders to explore:
folders = ["/home/user"]
while !folders.empty?
current = folders.pop
puts current
# In real code, you would add subfolders:
# Dir.foreach(current) { |f| folders << "#{current}/#{f}" if f != "." && f != ".." }
end
Using pop for directory traversal turns the list of folders into a work queue where each item is consumed as it gets processed. The loop continues until the list is exhausted, producing a depth-first exploration order without needing explicit recursion or a separate stack variable to track progress through the directory tree.
Return Value
pop returns the removed element. When called with a number, it returns an array of removed elements:
# Without argument
[1, 2, 3].pop
# => 3
# With argument
[1, 2, 3].pop(2)
# => [2, 3]
The returned element(s) are copies — modifying them does not affect the original collection. For value types like integers and symbols this distinction rarely matters, but with mutable objects like hashes and strings, understanding that you get a reference to the same object helps avoid unintended side effects when you mutate the popped result elsewhere in your code.
original = [{name: "Alice"}]
popped = original.pop
popped[:name] = "Bob"
original
# => []
Performance Notes
pop is amortized O(1). Ruby collections maintain a buffer of extra capacity at the end, so removing the last element usually does not require reallocation or moving other elements. The array simply decrements its internal length counter.
Only when the collection’s size drops below a certain threshold relative to its capacity might Ruby release the excess memory — but this does not affect the O(1) removal time for individual calls.
This makes pop extremely efficient for stack-like usage patterns. Unlike operations near the beginning of a sequence (which require shifting all remaining elements), removing from the end is a constant-time operation.
See Also
- /reference/array-methods/array-push/ — add elements to the end of a collection
- /reference/array-methods/array-select/ — filter elements within a list by condition
- /reference/enumerable/enumerable-reduce/ — reduce an array to a single accumulated value