Working with arrays in Ruby: a beginner tutorial
Arrays are one of the most fundamental data structures in Ruby. They store collections of items (numbers, strings, objects, or even nested arrays) in an ordered list with integer indices starting at zero. This tutorial covers what you need to work with arrays effectively, from the basic literal syntax to the higher-order operations the Enumerable mixin layers on top.
We start with creation and indexing, move through mutation and the standard <<, push, pop, shift, unshift operations, then graduate to each, map, select, reject, and inject. The set operators |, &, and - get their own section because they are quietly useful for deduplication and difference checks. The tutorial closes with two recurring pitfalls: shared references when you assign an array to a new variable, and the difference between mutating and non-mutating method names (the ones ending in !). For deeper coverage of individual methods, see the Enumerable map reference and the array-reject reference.
Creating arrays
The simplest way to create an array is using square brackets:
fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = [1, "two", :three, true]
You can also use the Array.new constructor:
empty = Array.new
three_nils = Array.new(3) # => [nil, nil, nil]
with_defaults = Array.new(3, 0) # => [0, 0, 0]
Ruby arrays can hold objects of any type, and they maintain the order in which you add items.
Accessing elements
Array indices start at zero. Use brackets to access elements:
fruits = ["apple", "banana", "cherry", "date"]
fruits[0] # => "apple"
fruits[1] # => "banana"
fruits[-1] # => "date" (last element)
fruits[-2] # => "cherry" (second to last)
fruits.first # => "apple"
fruits.last # => "date"
fruits[1, 2] # => ["banana", "cherry"] # slice
Negative indices count from the end of the array, making it easy to access elements from the back without knowing the array length.
Modifying arrays
Ruby provides many methods to add, remove, and modify elements:
numbers = [1, 2, 3]
numbers.push(4) # => [1, 2, 3, 4]
numbers << 5 # => [1, 2, 3, 4, 5] # shovel operator
numbers.pop # => 5, numbers is now [1, 2, 3, 4]
numbers.shift # => 1, numbers is now [2, 3, 4]
numbers.unshift(0) # => [0, 2, 3, 4]
numbers.insert(1, 99) # => [0, 99, 2, 3, 4]
numbers.delete(99) # => [0, 2, 3, 4]
The shovel operator (<<) is particularly idiomatic in Ruby and is used frequently.
Iterating over arrays
The each method is the most common way to iterate:
fruits = ["apple", "banana", "cherry"]
fruits.each do |fruit|
puts fruit
end
# Prints: apple, banana, cherry (each on new line)
# Short block syntax
fruits.each { |f| puts f.upcase }
Use map when you need to transform each element:
numbers = [1, 2, 3, 4, 5]
squares = numbers.map { |n| n ** 2 }
# => [1, 4, 9, 16, 25]
doubled = numbers.map(&:*)
# => [2, 4, 6, 8, 10]
The map method returns a new array with the transformed values, leaving the original unchanged.
Filtering and finding
Select elements based on conditions:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even = numbers.select { |n| n.even? }
# => [2, 4, 6, 8, 10]
odd = numbers.reject { |n| n.even? }
# => [1, 3, 5, 7, 9]
first_big = numbers.find { |n| n > 5 }
# => 6
The find method returns the first matching element, while select returns all matches.
Useful array methods
Ruby arrays come with many built-in methods:
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
numbers.length # => 8
numbers.count # => 8
numbers.empty? # => false
numbers.include?(5) # => true
numbers.sort # => [1, 1, 2, 3, 4, 5, 6, 9]
numbers.reverse # => [6, 2, 9, 5, 4, 1, 3, 1]
numbers.sum # => 31
numbers.max # => 9
numbers.min # => 1
Common operations like checking if an array contains something, finding its size, or getting min/max values are built-in.
Combining arrays
Join arrays together:
a = [1, 2]
b = [3, 4]
a + b # => [1, 2, 3, 4]
a.concat(b) # => [1, 2, 3, 4], a is modified
[1, 2] | [2, 3] # => [1, 2, 3] # union
[1, 2, 2, 3] & [2, 3, 3] # => [2, 3] # intersection
[1, 2] - [2] # => [1] # difference
The union and intersection operators are useful for set operations on arrays.
Common pitfalls
One mistake beginners make is confusing each with map. The each method returns the original array, while map returns a new array with transformed values:
numbers = [1, 2, 3]
numbers.each { |n| n * 2 } # => [1, 2, 3] # ignored return value
numbers.map { |n| n * 2 } # => [2, 4, 6] # new array
Another issue involves modifying an array while iterating over it. If you need to modify during iteration, work with a copy:
numbers = [1, 2, 3, 4, 5]
# Bad - unexpected behavior
numbers.each { |n| numbers.delete(n) if n.even? }
# Good - work with a copy
numbers.dup.each { |n| numbers.delete(n) if n.even? }
When to use arrays
Arrays are perfect when you need:
- An ordered collection of items
- Quick access by index
- Iterating over a list of similar items
- Simple data structures
For key-value pairs, use hashes instead. For sorted data with fast lookup, consider sets.
Summary
Arrays are versatile and essential in Ruby. They provide efficient indexed access, powerful iteration methods, and work seamlessly with Ruby’s enumerable module. Master these basics and you’ll manipulate data confidently in any Ruby project.
What’s next
Now that you understand arrays, the next logical step is learning about hashes—Ruby’s key-value data structure. Head to Hashes in Ruby to continue your journey through Ruby fundamentals.