Overview
Ruby provides multiple approaches for creating and initializing arrays. The Array class serves as Ruby's primary ordered collection type, supporting both literal syntax and constructor methods for creation. Arrays in Ruby are dynamic, heterogeneous collections that can grow and shrink during runtime.
The core creation methods include array literals ([]
), the Array()
method, Array.new
, and specialized methods like Array.of
and range-based creation. Each approach offers different capabilities for initial size, default values, and population strategies.
# Literal syntax
numbers = [1, 2, 3, 4, 5]
# Constructor method
words = Array.new(3, "hello")
# => ["hello", "hello", "hello"]
# Block-based initialization
squares = Array.new(5) { |i| i * i }
# => [0, 1, 4, 9, 16]
Basic Usage
Literal Array Creation
The most common array creation uses square bracket syntax. This approach handles mixed data types and nested structures naturally.
# Empty array
empty = []
# Mixed types
mixed = [1, "string", :symbol, true, nil]
# Nested arrays
matrix = [[1, 2], [3, 4], [5, 6]]
# Using variables
x, y = 10, 20
coordinates = [x, y, x + y]
# => [10, 20, 30]
Array.new Constructor
The Array.new
constructor provides control over initial size and default values. The method accepts size and default value parameters.
# Fixed size with default value
zeros = Array.new(5, 0)
# => [0, 0, 0, 0, 0]
# Size without default (nil elements)
empty_slots = Array.new(3)
# => [nil, nil, nil]
# Block initialization for unique objects
arrays = Array.new(3) { [] }
# => [[], [], []]
Range and Splat Operations
Arrays can be created from ranges and other enumerable objects using splat operators and conversion methods.
# Range to array
digits = (0..9).to_a
# => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Splat operator
range_array = [*1..5]
# => [1, 2, 3, 4, 5]
# String splitting
letters = "hello".chars
# => ["h", "e", "l", "l", "o"]
Array() Conversion Method
The Array()
method converts various objects into arrays, handling nil values and already-existing arrays gracefully.
# Convert nil
Array(nil)
# => []
# Convert single value
Array("hello")
# => ["hello"]
# Convert existing array (returns same object)
existing = [1, 2, 3]
Array(existing).object_id == existing.object_id
# => true
Advanced Usage
Block-Based Initialization Patterns
Complex initialization logic can be encapsulated in blocks passed to Array.new
. This enables computed values, object creation, and conditional logic during array population.
# Fibonacci sequence
fibonacci = Array.new(10) do |i|
case i
when 0, 1 then i
else fibonacci[i-1] + fibonacci[i-2]
end
end
# => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
# Object initialization
class Point
attr_accessor :x, :y
def initialize(x, y)
@x, @y = x, y
end
end
points = Array.new(5) { |i| Point.new(i, i * 2) }
# Random data generation
random_data = Array.new(100) { rand(1..1000) }
Multi-dimensional Array Creation
Creating and initializing multi-dimensional arrays requires careful attention to object sharing and block usage.
# Correct: each sub-array is unique
matrix = Array.new(3) { Array.new(3, 0) }
matrix[0][0] = 1
# matrix[1][0] and matrix[2][0] remain 0
# Incorrect: all rows share the same array object
shared_matrix = Array.new(3, Array.new(3, 0))
shared_matrix[0][0] = 1
# All rows now have [1, 0, 0] because they're the same object
# Complex 3D initialization
cube = Array.new(3) do |x|
Array.new(3) do |y|
Array.new(3) { |z| "#{x},#{y},#{z}" }
end
end
Factory Methods and Custom Creators
Creating specialized array factory methods enables consistent initialization patterns across applications.
class ArrayFactory
def self.coordinate_grid(width, height)
Array.new(height) do |y|
Array.new(width) { |x| [x, y] }
end
end
def self.weighted_random(size, weights)
total = weights.sum
Array.new(size) do
r = rand * total
weights.find_index { |w| (r -= w) <= 0 }
end
end
def self.circular_buffer(size, initial = nil)
Array.new(size, initial).tap do |arr|
arr.define_singleton_method(:add) do |item|
self[@index] = item
@index = (@index + 1) % size
end
arr.instance_variable_set(:@index, 0)
end
end
end
grid = ArrayFactory.coordinate_grid(3, 2)
# => [[[0, 0], [1, 0], [2, 0]], [[0, 1], [1, 1], [2, 1]]]
Performance & Memory
Memory Allocation Patterns
Array creation involves memory allocation that varies based on initialization method and size. Understanding these patterns helps optimize memory usage in performance-critical code.
require 'benchmark'
# Benchmarking different creation methods
Benchmark.bm(15) do |x|
size = 100_000
x.report("literal small:") { 1000.times { [1, 2, 3, 4, 5] } }
x.report("Array.new:") { 1000.times { Array.new(5, 1) } }
x.report("Array.new block:") { 1000.times { Array.new(5) { |i| i + 1 } } }
x.report("large literal:") { [*1..size] }
x.report("large new:") { Array.new(size) { |i| i + 1 } }
x.report("large range:") { (1..size).to_a }
end
Pre-allocation and Growth Strategies
Ruby arrays grow dynamically, but pre-allocation can improve performance when the final size is known.
# Inefficient: multiple reallocations
slow_array = []
10_000.times { |i| slow_array << i }
# More efficient: pre-allocated
fast_array = Array.new(10_000)
10_000.times { |i| fast_array[i] = i }
# Memory-efficient initialization for large datasets
def create_large_dataset(size)
# Reserve capacity to avoid reallocations
Array.new(size) do |i|
# Expensive operation result
expensive_computation(i)
end
end
Object Reference Considerations
Understanding when arrays share references versus creating unique objects affects both memory usage and program behavior.
# Reference sharing (memory efficient but potentially problematic)
shared_arrays = Array.new(1000, [])
shared_arrays[0] << "item" # All arrays now contain "item"
# Unique objects (higher memory usage but safe)
unique_arrays = Array.new(1000) { [] }
unique_arrays[0] << "item" # Only first array contains "item"
# Memory usage comparison
ObjectSpace.count_objects[:T_ARRAY] # Check array count before/after
Common Pitfalls
Shared Reference Gotcha
The most common pitfall in array creation involves unintended object sharing when using default values with Array.new
.
# Problem: shared references
problem_matrix = Array.new(3, Array.new(3, 0))
problem_matrix[0][0] = 1
# All rows now show [1, 0, 0] - they're the same object!
# Solution: use blocks for object creation
solution_matrix = Array.new(3) { Array.new(3, 0) }
solution_matrix[0][0] = 1
# Only first row shows [1, 0, 0]
# Verification
problem_matrix[0].object_id == problem_matrix[1].object_id # => true
solution_matrix[0].object_id == solution_matrix[1].object_id # => false
Block Parameter Confusion
Block parameters in Array.new
represent the current index, not the element value. This distinction confuses developers expecting element-based iteration.
# Incorrect expectation: element-based thinking
wrong = Array.new(5) { |element| element * 2 }
# Developer expects: [0, 2, 4, 6, 8] but gets [0, 2, 4, 6, 8]
# This works by coincidence because index equals expected value
# Correct understanding: index-based
correct = Array.new(5) { |index| (index + 1) * 10 }
# => [10, 20, 30, 40, 50]
# Common mistake with external data
data = ['a', 'b', 'c', 'd', 'e']
# Wrong: tries to use data as if block param is element
Array.new(5) { |elem| elem.upcase } # Fails: integers don't have upcase
# Right: use index to access external data
Array.new(5) { |i| data[i]&.upcase } # => ["A", "B", "C", "D", "E"]
Performance Anti-patterns
Certain creation patterns create performance bottlenecks, especially with large datasets or repeated operations.
# Anti-pattern: building arrays with repeated concatenation
def slow_build(n)
result = []
n.times { |i| result += [i] } # Creates new array each time
result
end
# Better: use append operations
def faster_build(n)
result = []
n.times { |i| result << i } # Modifies existing array
result
end
# Best: pre-allocate when size is known
def fastest_build(n)
Array.new(n) { |i| i } # Single allocation
end
# Anti-pattern: nested array creation without blocks
def create_matrix_wrong(size)
row = Array.new(size, 0)
Array.new(size, row) # All rows share the same array!
end
# Correct pattern: unique objects per row
def create_matrix_right(size)
Array.new(size) { Array.new(size, 0) }
end
Reference
Array Creation Methods
Method | Parameters | Returns | Description |
---|---|---|---|
[] |
*elements |
Array |
Creates array with specified elements |
Array.new |
size=0, default=nil |
Array |
Creates array with size and default value |
Array.new |
size, &block |
Array |
Creates array with size, calling block for each element |
Array() |
obj |
Array |
Converts object to array |
Array.[] |
*elements |
Array |
Synonym for [] |
%w[] |
string_literal |
Array<String> |
Creates string array from whitespace-separated words |
%W[] |
string_literal |
Array<String> |
Like %w but allows interpolation |
%i[] |
symbol_literal |
Array<Symbol> |
Creates symbol array from whitespace-separated words |
%I[] |
symbol_literal |
Array<Symbol> |
Like %i but allows interpolation |
Conversion Methods
Method | Source Type | Returns | Description |
---|---|---|---|
to_a |
Range, Enumerator |
Array |
Converts enumerable to array |
chars |
String |
Array<String> |
Splits string into character array |
bytes |
String |
Array<Integer> |
Converts string to byte value array |
lines |
String |
Array<String> |
Splits string into line array |
split |
String |
Array<String> |
Splits string on pattern |
scan |
String |
Array<String> |
Finds pattern matches in string |
Special Syntax Forms
Syntax | Usage | Result | Notes |
---|---|---|---|
[*enum] |
[*1..5] |
[1, 2, 3, 4, 5] |
Splat operator expands enumerable |
%w[words] |
%w[red blue green] |
["red", "blue", "green"] |
Word array literal |
%W[words] |
%W[#{color} blue] |
["red", "blue"] |
Interpolated word array |
%i[syms] |
%i[read write execute] |
[:read, :write, :execute] |
Symbol array literal |
%I[syms] |
%I[#{mode} execute] |
[:read, :execute] |
Interpolated symbol array |
Common Patterns
Pattern | Code | Use Case |
---|---|---|
Empty array | [] |
Starting point for dynamic arrays |
Fixed size | Array.new(n, value) |
Pre-allocated arrays with default |
Computed values | Array.new(n) { block } |
Arrays with calculated elements |
Range conversion | (start..end).to_a |
Numeric sequences |
String splitting | string.split(pattern) |
Text processing |
Multi-dimensional | Array.new(n) { Array.new(m) } |
Matrices and grids |