Array#append
arr.append(*objects) -> arr Array#append adds one or more elements to the end of an array, mutating it in place. It returns the same array object, allowing method chaining.
Syntax
array.append(*objects)
The basic example shows a single element being appended to a two-item list. The method call returns the modified receiver, which is why the next line inspects fruits directly rather than capturing the return value. This in-place mutation is the defining characteristic of append: the original object changes, and the return value is just a convenience for chaining rather than a new copy.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
*objects | Object | Required | One or more objects to add to the receiver |
Examples
Basic usage
fruits = ["apple", "banana"]
fruits.append("cherry")
fruits
# => ["apple", "banana", "cherry"]
The multiple-elements example passes three integers to append in a single call. The splat parameter *objects collects them all, so you can pass as many arguments as you need without wrapping them in a separate structure. This is more convenient than calling push with individual arguments when you have a batch of values to add at once, and it reads more naturally in code that is assembling a list from several sources.
Adding multiple elements
numbers = [1, 2, 3]
numbers.append(4, 5, 6)
numbers
# => [1, 2, 3, 4, 5, 6]
The chaining example starts with an empty literal [] and calls append twice in a row. Because the method returns the receiver, each call passes the same object forward. This pattern works well in builder-style code where you want to construct a list in a single expression. Keep in mind that the mutation still happens in place, so any other reference to the same object will see the new elements.
Method chaining
result = [].append(1).append(2)
result
# => [1, 2]
The incremental building example shows append being called three times in sequence, which is a common pattern when items arrive one at a time from a loop or callback. Each call adds to the end, so the final order matches the insertion order. This is the simplest way to accumulate results when you do not know the count ahead of time.
Common Patterns
Building incrementally
items = []
items.append("first")
items.append("second")
items.append("third")
# => ["first", "second", "third"]
The stack example pairs append with pop, which is the classic LIFO pattern in Ruby. Because append adds to the end and pop removes from the end, the combination gives you an array-based stack without needing a separate data structure. This is a lightweight alternative to defining a Stack class when all you need is push and pop.
Stack-like behavior with append
stack = []
stack.append("first")
stack.append("second")
stack.pop # => "second"
The nil/false example demonstrates that append treats all values equally, including nil and false. Neither value is filtered or rejected, which means you can build sparse or sentinel-bearing lists without special-casing falsy elements. This is consistent with Ruby’s general approach of treating nil and false as ordinary values rather than special markers to be stripped.
Edge Cases
Adding nil or false
arr = [1, 2]
arr.append(nil)
arr.append(false)
arr
# => [1, 2, nil, false]
The empty-collection example shows that append works correctly even when the receiver starts with zero elements. The first call adds the initial element, and subsequent calls continue to grow the list from there. This is a common initialization pattern: start with [], then append items as they become available.
Adding to an empty collection
arr = []
arr.append("first")
arr
# => ["first"]
The mixed-types example shows that append accepts any Ruby object without restriction. You can push numbers, strings, symbols, nested structures, and even other collections into the same receiver. This flexibility is useful for result aggregators and data pipelines, though in practice most well-structured code keeps the elements of a single list to a consistent type for easier downstream processing.
Adding mixed types
mixed = []
mixed.append(1, "two", :three, [4, 5], {six: 6})
mixed
# => [1, "two", :three, [4, 5], {:six=>6}]
The mixed-types example shows that append accepts any Ruby object without restriction. You can push numbers, strings, symbols, nested structures, and even other collections into the same receiver. This flexibility is useful for result aggregators and data pipelines, though in practice most well-structured code keeps the elements of a single list to a consistent type for easier downstream processing.
Performance Notes
append mutates the receiver in place, which is more memory-efficient than creating fresh collections with + or concatenation. It runs in amortized O(1) time. The amortized constant-time guarantee means that even when Ruby needs to reallocate the underlying storage, the average cost per append stays flat as the collection grows, which makes it safe to use in tight loops and long-running processes.
choosing append intentionally
append works best when the code is already treating the array as a growing collection. The method says very plainly that new values belong at the end, which makes it a good fit for builders, queues, and incremental assembly. Because it mutates the original array, the surrounding code should make that side effect obvious so later readers know the array is supposed to change. That helps avoid surprises when the same object is shared across several methods.
It is also a nice option when you want the return value to remain the same array for chaining. In those cases the flow can stay compact without hiding what happened to the underlying object. If the code needs to build a fresh collection instead, a non-mutating approach is usually easier to reason about.
when in-place growth is the right choice
An in-place append is easiest to follow when the array already represents a working list rather than a reusable template. A parser, a queue, or a result buffer often fits that description because each new item belongs on the end of the same object. The method keeps the code short and makes the data flow feel natural, as long as the mutation is obvious to the reader. If later code should not see the change, build a new array instead.