Fix an Array Index Out of Bounds Exception in Ruby?

This code reads a file, splits the data into blocks (representing journeys), and processes each block line by line. It extracts destination information from specific lines and prints them, skipping blocks that contain the word “position.” A list of journey objects is meant to be created but remains commented out.

Ruby Code (with Errors):

code# save text file to string 
data = File.read("workdata.txt")

# split string into blocks of text relevant to each journey
journeys = data.split(/\n\s\n/)

# store the amount of journeys as a variable called journeys_size
journeys_size = journeys.length

# split each journey into lines and save to an array called "journey_lines"
@journey_lines = journeys.map { |i| i.split(/\n/) }

# create an array called "all_journey_objects" to hold all journeys
all_journey_objects = []

# step through the journey arrays
@journey_lines.each do |line|

next if line[0].include?("position") # skip the journey block of text if it contains position

destinations = []
destination1 = line[12].upcase
destination2 = line[13].scan(/[a-z]+/i).join(" ").upcase
# destination3 = line[14].scan(/[a-z]+/i).join(" ").upcase # <---- When i uncomment this line **
# destination4 = line[15].scan(/[a-z]+/i).join(" ").upcase
# destination5 = line[16].scan(/[a-z]+/i).join(" ").upcase

puts destination1
puts destination2
# puts destination3 # <---- When i uncomment this line **
# puts destination5
# puts destination4

# journey = Journey.new(line[0] , line[1] , line[6] , destinations, etas, windows)
# all_journey_objects << journey
end

Corrected Code:

code# Save the text file to a string
data = File.read("workdata.txt")

# Split string into blocks of text relevant to each journey
journeys = data.split(/\n\s*\n/)

# Store the number of journeys in a variable called journeys_size
journeys_size = journeys.length

# Split each journey into lines and save it to an array called "journey_lines"
@journey_lines = journeys.map { |i| i.split(/\n/) }

# Create an array called "all_journey_objects" to hold all journeys
all_journey_objects = []

# Step through the journey arrays
@journey_lines.each do |line|

# Skip the journey block of text if it contains the word 'position'
next if line[0].include?("position")

destinations = []

# Ensure the line index exists before accessing
destination1 = line[12]&.upcase || "N/A"
destination2 = line[13]&.scan(/[a-z]+/i)&.join(" ")&.upcase || "N/A"
destination3 = line[14]&.scan(/[a-z]+/i)&.join(" ")&.upcase || "N/A"
destination4 = line[15]&.scan(/[a-z]+/i)&.join(" ")&.upcase || "N/A"
destination5 = line[16]&.scan(/[a-z]+/i)&.join(" ")&.upcase || "N/A"

puts destination1
puts destination2
puts destination3
puts destination4
puts destination5

# You can add the destinations to the array or proceed with other logic
destinations << destination1 << destination2 << destination3 << destination4 << destination5

# Example of how you might create a journey object (if relevant)
# journey = Journey.new(line[0], line[1], line[6], destinations)
# all_journey_objects << journey
end

Explanation

When working with arrays in Ruby, one of the most common issues developers encounter is the dreaded “Array Index Out Of Bounds” exception. This error occurs when you try to access an element in an array that doesn’t exist, i.e., you try to access an index that is beyond the size of the array.

In the code above, we were iterating over each journey and trying to access specific lines (like line 12, 13, etc.). The problem arises when a journey block doesn’t have enough lines, and trying to access a non-existent index causes the program to crash with an “Array Index Out Of Bounds” exception.

The Fix:

To avoid this error, we can safely access array elements using the & operator (safe navigation) and add a fallback value ("N/A" in our case) if the index does not exist. This prevents the program from crashing and allows it to handle journeys with different numbers of lines.

Here’s how we applied the fix:

codedestination1 = line[12]&.upcase || "N/A"
destination2 = line[13]&.scan(/[a-z]+/i)&.join(" ")&.upcase || "N/A"

By using &. (safe navigation operator), we ensure that Ruby only attempts to call methods like upcase or scan if the index exists. Otherwise, it returns nil, and then we provide a fallback value of "N/A" using the || operator.

This simple adjustment ensures that the code handles cases where certain lines in the journey data may be missing or shorter than expected.

The End

Always remember to check whether an array index exists before accessing it, especially when working with data that can have variable lengths like text files. Ruby’s safe navigation operator (&.) and fallback values (||) are invaluable tools to avoid crashes and ensure your code runs smoothly across all scenarios.

Related blog posts