5 Replies - 1773 Views - Last Post: 23 March 2015 - 11:46 PM Rate Topic: -----

#1 lepinat0r   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 40
  • Joined: 21-December 10

Working with 2d arrays and trying to change 'touching' indexes

Posted 23 March 2015 - 12:53 PM

I'm newer to ruby and trying to do this challenge. So, a 2d array is passed in:

[[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]]




The goal is to find the 1's and then make the 'touching' indexes turn to 1's if they are not already. So, the output would be:

[[0, 1, 0, 0],
[1, 1, 1, 1],
[0, 1, 1, 1],
[0, 0, 0, 1]]




I tried to do this going through a double .each block, but I found once I changed one index, the next iteration would think that the index was a 1 to begin with and would change everything around that.

So, my next/current thoughts are to store the coords of the 1's and then after all the iterations are done, perform the operation on the stored coords. The problem I am having is I am not sure how to pass in stored coords as an array index since they are not considered integers..I'm sorry if this sounds confusing, here is a little bit of code to show what I am trying:

@arr.each_with_index do |row, row_num|
			row.each_with_index do |col, col_num|
				if (col == 1)
					# Store locations of 1s
					@pos.push([row_num, col_num])

				end
			end
		end
    
    # Here i need to access the stored @pos in @arr and edit the indexes



Thank you in advance. If there is a much easier way to do this, I would be happy to hear!

Is This A Good Question/Topic? 0
  • +

Replies To: Working with 2d arrays and trying to change 'touching' indexes

#2 sepp2k   User is offline

  • D.I.C Lover
  • member icon

Reputation: 2754
  • View blog
  • Posts: 4,411
  • Joined: 21-June 11

Re: Working with 2d arrays and trying to change 'touching' indexes

Posted 23 March 2015 - 01:37 PM

The easiest approach would be to not modify the array in-place, but rather create a new with the new values. I can think of two relatively straight-forward ways to accomplish this:

1. Use nested calls to map.with_index and then inside the block produce 1 iff the corresponding element in the old array or any of its neighbors are 1.
2. Create a nested array of zeros (new_array = Array.new(num_rows) { Array.new(num_cols, 0) }), then iterate over the original array using each_with_index (just like you are doing now) and whenever you encounter a 1, set the corresponding element in the new array as well as its neighbors to 1.

Your approach should work too though. What problem are you encountering with it?
Was This Post Helpful? 1
  • +
  • -

#3 lepinat0r   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 40
  • Joined: 21-December 10

Re: Working with 2d arrays and trying to change 'touching' indexes

Posted 23 March 2015 - 02:03 PM

I will look into your approaches I don't quite understand them right now, but I will try to wrap my head around them for a bit.

The problem with my solution is that @pos[0] equals [1, 1], but I don't know how to pass it into the @arr value like @arr[@pos0]]. In theory I thought it would be equivalent to @arr[1, 1], but obviously it isn't that smart. I was just going to loop through each @pos' and then apply the index changes that way.
Was This Post Helpful? 0
  • +
  • -

#4 sepp2k   User is offline

  • D.I.C Lover
  • member icon

Reputation: 2754
  • View blog
  • Posts: 4,411
  • Joined: 21-June 11

Re: Working with 2d arrays and trying to change 'touching' indexes

Posted 23 March 2015 - 03:05 PM

Ah, I see. You can either access the items by index like this: arr[ pos[0], pos[1] ] (where pos is an element of @pos, which really should be called @positions or at least @poss to make clear that it contains multiple position - also does it really need to be an instance variable? Where else do you need to access it?). Or you can unpack the array directly when iterating like this:

@positions.each do |row_num, col_num|
  # ...
end


Was This Post Helpful? 1
  • +
  • -

#5 lepinat0r   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 40
  • Joined: 21-December 10

Re: Working with 2d arrays and trying to change 'touching' indexes

Posted 23 March 2015 - 03:24 PM

View Postsepp2k, on 23 March 2015 - 03:05 PM, said:

Ah, I see. You can either access the items by index like this: arr[ pos[0], pos[1] ] (where pos is an element of @pos, which really should be called @positions or at least @poss to make clear that it contains multiple position - also does it really need to be an instance variable? Where else do you need to access it?). Or you can unpack the array directly when iterating like this:

@positions.each do |row_num, col_num|
  # ...
end



I agree that the variable should be named differently and it doesn't need to be an instance variable. What I did not know is you can use multiple variable in an .each statement like that... That makes things so clear and what I was trying to do from the start...

positions = []

		@arr.each_with_index do |row, row_num|
			row.each_with_index do |col, col_num|
				if (col == 1)
					# Store locations of 1s to blur
					positions.push([row_num, col_num])

				end
			end
		end
		# Obviously need to error check for OOB
		positions.each do |row_num, col_num|
			@arr[row_num+1][col_num] = 1
			@arr[row_num-1][col_num] = 1
			@arr[row_num][col_num-1] = 1
			@arr[row_num][col_num+1] = 1
		end



Thank you so much!
Was This Post Helpful? 0
  • +
  • -

#6 Lemur   User is offline

  • Pragmatism over Dogma
  • member icon


Reputation: 1453
  • View blog
  • Posts: 3,633
  • Joined: 28-November 09

Re: Working with 2d arrays and trying to change 'touching' indexes

Posted 23 March 2015 - 11:46 PM

Let' s take a functional ride on this, shall we?

First we're going to want to get a list of coordinates that have 1 in them:
arr = [
  [0, 0, 0, 0],
  [0, 1, 0, 0],
  [0, 0, 0, 1],
  [0, 0, 0, 0]
]

coordinates = arr.each_with_index.map { |row, row_index|
  row.each_with_index.reduce([]) { |row_coordinates, (item, col_index)|
    item == 1 ? row_coordinates.concat([row_index, col_index]) : row_coordinates
  }
}.reject(&:empty?)



Then we're going to want to get all the surrounding points. In this I just used a quick lambda and a closure to capture our bounds so as to not recompute them every loop.
surrounding = -> x_bound, y_bound {
  x_bounds = (0...x_bound)
  y_bounds = (0...y_bound)

  -> coords {
    x_coord, y_coord = coords

    [
      [x_coord + 1, y_coord],
      [x_coord - 1, y_coord],
      [x_coord, y_coord + 1],
      [x_coord, y_coord - 1]
    ].select { |(x, y)| y_bounds.include?(y) && x_bounds.include?(x) } # We select only points that are within the bounds
  }
}.call(arr.length, arr.first.length) # This function closes over the x_bounds and y_bounds, caching them for later use and returns a function for us to use.




Now we want the positions where a one will be, which would be the concatenation of the surrounding points and the original coordinates we found:
one_coords = coordinates.flat_map(&surrounding).uniq.concat(coordinates) 



Then we just make ourselves a new array:
new_array = Array.new(arr.length) { |row|
  Array.new(arr.first.length) { |col|
    one_coords.include?([row, col]) ? 1 : 0
  }
}

# => [
#   [0, 1, 0, 0],
#   [1, 1, 1, 1],
#   [0, 1, 1, 1],
#   [0, 0, 0, 1]
# ]



Call this more of a thought exercise to see if it can be done without mutation and in more of a functional style. More of I just wanted to see how it would be done. Any more I tend to value code that won't transform things as it's far easier to test later and run in parallel if the time should come (think Sidekiq, ActionController::Live, and a few others which are picky on mutations)

Admittedly I've spent a bit too much time tinkering around Elixir, Scala, and Haskell lately so I have a few new habits.

This post has been edited by Lemur: 23 March 2015 - 11:47 PM

Was This Post Helpful? 1
  • +
  • -

Page 1 of 1