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 0s and 1s with each individual array stacked vertically on top of one another. Then, to locate all the 1s and change the cardinal 0s to 1s 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.