rubyguides

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.