rubyguides

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

ParameterTypeDefaultDescription
*objectsObjectRequiredOne 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.

See Also