1

I have an array of arrays:

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

I want to find the index (row and column) of the items with value 1. How can I do this? I need these values so that I can manipulate the cells at either side of the 1s. I looked at find.index method, but I'm not sure.

2
  • I wonder why people keep using a nested array for these kinds of situations. A flat array is much easier to handle. Commented Feb 26, 2016 at 15:40
  • 1
    When you give an example please assign a variable to each input object. That way the variable can be referenced in answers and comments. All but one of the answers begins by defining the array. Had you written arr =[[0,0....]] none of that would be needed--answers would have just referenced arr. I realize you're new to SO. This is just a tip, not a criticism. Commented Feb 26, 2016 at 19:41

5 Answers 5

3

Not that fancy, but you could use two loops:

ary = [ [0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0] ] result = [] ary.each_with_index do |row, i| row.each_with_index do |value, j| result << [i, j] if value == 1 end end result #=> [[1, 1], [2, 3]] 
Sign up to request clarification or add additional context in comments.

1 Comment

...or ary.each_index {|i| ary.first.each_index {|j| result << [i,j] if ary[i][j] == 1}}.
2
a = [ [0,0,0,0], [0,1,0,0], [0,0,0,1], [0,0,0,0] ].flatten a.each.with_index.select{|e, _| e == 1}.map{|_, i| i.divmod(4)} # => [[1, 1], [2, 3]] 

To be interpreted as (row 1, column 1) and (row 2, column 3).

2 Comments

@Stefan It is possible to get 4 as the length of the first element of the original array, but my suggestion is to have a flat array from the beginning, with the additional information of 4.
Either that or a hash with [x, y] => value pairs
1

Consider using the Matrix class.

require 'matrix' arr = [ [0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0] ] target = 1 Matrix[*arr].each_with_index.with_object([]) { |(e,row,col),a| a << [row,col] if e==target } #=> [[1, 1], [2, 3]] 

I like how this reads.

2 Comments

Thanks for all your help everyone
A combination of select and map would also work: m.each_with_index.select { |e, r, c| e == 1 }.map { |e, r, c| [r, c] }
0

Ruby has a very flexible, functional style Enumerable module which is included in many of the standard collections (Arrays, Hashes, Sets). You can find the documentation at ruby-doc.org.

A nice one-liner to solve this in max #rows * 2 #columns time would be the following:

matrix .each.with_index .inject([]) { |acc, (el,idx)| el.include?(1) ? acc.push([idx, el.index(1)]) : acc } # => [[1,1],[2,3]] 

1 Comment

This doesn't work if a row contains more than one 1
0
z = [ [0,0,0,0], [0,1,0,0], [0,0,0,1], [0,0,0,0] ] 

First, let's find the matching rows:

rows = z.each_with_index.select { |row, index| row.include? 1}.map(&:last) # => [1, 2] 

And then for each row let's find the index of the matching 1:

cols = rows.map {|row| z[row].each_with_index.select { |item, index| item == 1}.map(&:last) } # => [[1], [3]] 

If we want, we can then combine them using zip:

rows.zip(cols) # => [[1, [1]], [2, [3]]] 

The above works even if a row of z contains multiple occurrences of 1

For example if we had:

z = [ [1,0,0,1], [0,1,0,0], [0,0,0,1], [1,1,1,0] ] 

then

rows = z.each_with_index.select { |row, index| row.include? 1}.map(&:last) # => [0, 1, 2, 3] cols = rows.map {|row| z[row].each_with_index.select { |item, index| item == 1}.map(&:last) } # => [[0, 3], [1], [3], [0, 1, 2]] rows.zip(cols) # => [[0, [0, 3]], [1, [1]], [2, [3]], [3, [0, 1, 2]]] 

1 Comment

Good point for implicitly pointing out a flaw in Timon Vonk's answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.