Deep Copies and Iterating on Arrays
Iterating on the array you’re currently working from
Part of The Firehose Project curriculum are a series of short coding challenges. The initial group is a series called Image Blur, which teaches you to work with and manipulate two dimensional arrays. The interesting part of this, is that it also teaches you how to manipulate data to appear properly in your terminal, stylizing it, so that it’s not simply printing out as pure data.
The challenge I was working on asked you to display a 2d array as a block of 0
s and 1
s with each individual array stacked vertically on top of one another. Then, to locate all the 1s and change the cardinal 0
s to 1
s as well.
I was struggling with the second challenge when my mentor and I got on a call. He’d noticed that I was trying to change the original array that I was looping through, and hitting some errors. If you try to change 0s into 1s from the original array, while simultaneously looping through that array, you can get stuck in some weird forever loops.
Make a copy instead
To solve this, I made a copy of my array and then looped through the original while editing the copy. There are a few options for creating copies in Ruby, I then learned. The Ruby docs suggest dup
and clone
and define them as:
In general, clone and dup may have different semantics in descendent classes. While clone is used to duplicate an object, including its internal state, dup typically uses the class of the descendent object to create the new instance.
I found a few posts explaining the differences, basically that clone is shallow while dup is supposedly not. However, after a few short tests in irb, it appeared that dup still wasn’t creating deep copies. As an alternative, I found Marshalling. If you Marshall load and then Marshal dump an object, you can create a deep copy of it with serialization.
<code>copy = Marshal.load( Marshal.dump(original) )</code>
This worked perfectly in this instance, however, Marshalling doesn’t work on all objects and has some security concerns, so you’re advised to be cautious when using it.
End result of an image blur
Here’s the full solution to the problem.
class Image
attr_accessor :image
def initialize(image)
@image = image
end
def output_image
copy = Marshal.load( Marshal.dump(image) )
image.each_with_index do |row, row_index|
# call each subarray index and item
row.each_with_index do |pixel, column_index|
if pixel == 1
if row_index < image.length-1
copy[row_index + 1][column_index] = 1
end
if row_index > 0
copy[row_index - 1][column_index] = 1
end
if column_index < row.length-1
copy[row_index][column_index + 1] = 1
end
if column_index > 0
copy[row_index][column_index - 1] = 1
end
end
end
end
copy.each_index do |i|
subarray = copy[i]
puts subarray.join
end
end
end
image = Image.new([
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]
])
image.output_image
Should result in
0100
1111
0111
0001
in terminal.