Search for zero in 2D array and make a corresponding row and col 0

332 views Asked by At

This is my code, which works, but it's too big. I want to refactor it.

req_row = -1
req_col = -1

a.each_with_index do |row, index|
  row.each_with_index do |col, i|
     if col == 0
        req_row = index
        req_col = i
        break
     end
  end
end

if req_col > -1 and req_row > -1
  a.each_with_index do |row,index|
    row.each_with_index do |col, i|
      print (req_row == index or i == req_col) ? 0 : col
       print " "
    end
    puts "\r"
  end
end

Input: 2D Array

1  2  3  4 
5  6  7  8
9  10 0 11
12 13 14 15  

Required output:

1  2  0  4 
5  6  0  8
0  0  0  0
12 13 0  15  
5

There are 5 answers

1
Cary Swoveland On BEST ANSWER

I'm surprised the Matrix class is not used more:

a = [[ 1,  2,  3,  4],
     [ 5,  6,  7,  8],
     [ 9, 10,  0, 11],
     [12, 13, 14, 15]]

require 'matrix'

m = Matrix.rows(a)
  #=> Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 11], [12, 13, 14, 15]]

r, c = m.index(0)
  #=> [2, 2]

Matrix.build(m.row_count, m.column_count) {|i,j| (i==r || j==c) ? 0 : m[i,j]}.to_a
  #=> [[ 1,  2, 0,  4],
  #    [ 5,  6, 0,  8],
  #    [ 0,  0, 0,  0],
  #    [12, 13, 0, 15]] 

Note Matrix objects are immutable. To change individual elements you must create a new matrix.

A slight modification is required if you wish to do this for every zero in the matrix:

a = [[ 1,  2,  3,  4],
     [ 5,  6,  7,  8],
     [ 9, 10,  0, 11],
     [ 0, 13, 14, 15]]

require 'set'

m = Matrix.rows(a)
  #=> Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 11], [0, 13, 14, 15]]

zero_rows = Set.new
zero_columns = Set.new
m.each_with_index { |e,i,j| (zero_rows << i; zero_columns << j) if e.zero? }
zero_rows
  #=> #<Set: {2, 3}> 
zero_columns
  #=> #<Set: {2, 0}>     

Matrix.build(m.row_count, m.column_count) do |i,j|
  (zero_rows.include?(i) || zero_columns.include?(j)) ? 0 : m[i,j]
end.to_a
  #=> [[0, 2, 0, 4],
  #    [0, 6, 0, 8],
  #    [0, 0, 0, 0],
  #    [0, 0, 0, 0]] 
3
sczizzo On

Based on the title of your question, here's solution that searches for positions of the zero values (fixate), then actually zeros out the appropriate row and column (clear, more aligned with the contents of your question):

def fixate matrix, search=0, replace=0
  rcs = []

  matrix.each_with_index do |row,r|
    row.each_with_index do |col,c|
      rcs << [ r, c ] if col == search
    end
  end

  rcs.each do |(row, col)|
    clear matrix, row, col, replace
  end

  matrix
end

def clear matrix, row, col, val=0
  matrix[row].map! { |_| val }     # Clear rows
  matrix.each { |r| r[col] = val } # Clear columns
  matrix
end

Quick test:

fixate [               # [
  [ 1,  2,  3,  4  ],  #   [ 1,  2,  0, 4  ],
  [ 5,  6,  7,  8  ],  #   [ 5,  6,  0, 8  ],
  [ 9,  10, 0,  11 ],  #   [ 0,  0,  0, 0  ],
  [ 12, 13, 14, 15 ]   #   [ 12, 13, 0, 15 ]
]                      # ]
1
Rebel Rider On

Try this code:

req_row = req_col = -1

a.each_with_index do |row, index|
    req_col = row.index(0) # searching index having value 0.
    if req_col
        req_row =  index 
        break
    end 
end
a.each_with_index do |row,index|
    row.each_with_index do |col, i|
        print ((req_row == index or i == req_col) ? 0 : col).to_s + " "
    end
    puts "\r"
end
0
Luke On

Here's what I came up with:

zero_rows=[]
a.map do |col| 
   col.each_with_index do |el, index|
     zero_rows.push(index) if el==0 
   end
   col.fill(0) if col.include?(0)
end

a.map{|col| zero_rows.each{|index| col[index]=0} }

First, use map to iterate through the columns and fill them with zeros if they contain at least one 0. but, while doing so, add the indexes which contain a 0 to the zero_rows array.

Afterwards, iterate through the array once more and set the indexes of each column that match an index in zero_rows to 0.

You may know the map method as collect. They do the same thing.

Side Note:

If an array contains multiple zero's this code will zero out every applicable column. OP's example and some other answers here will only zero out the first column in which a 0 is found. If this is the behavior you are expecting, then see @Doguita's answer.

4
Doguita On

I don't know if this code is better than the other answers. I will test it later:

ary = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 11], [12, 13, 14, 15]]
col = nil
row = ary.index{ |a| col = a.index(0) }
ary.each_with_index { |a, i| i == row ? a.fill(0) : a[col] = 0 } if col

p ary
# => [[1, 2, 0, 4], [5, 6, 0, 8], [0, 0, 0, 0], [12, 13, 0, 15]] 

Obs: This answer assumes there's only one 0 to search in the array