CrackedRuby logo

CrackedRuby

Array Creation and Initialization

A comprehensive guide covering Ruby's array creation syntax, initialization methods, and performance characteristics.

Core Built-in Classes Array Class
2.4.1

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