ArafatK ArafatK - 5 months ago 16
Ruby Question

Simple Way to Check if the Ruby array is a matrix?

I am working on a project that involves checking if the input is a n-dimensional matrix(and find its dimensions) and raise error if not. For example

arr = [ [[1,2],[3,4]], [[5,6],[7,8]], [[9,10],[11,12]] ]


is a matrix of dimensions [3 2 2]. What would be the simplest generic way to do that ?

Answer

A recursive solution, but not pretty easy to understand.

arr1 = [[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]]
arr2 = [[[1, 2], [4]], [6, [7, 8]]]

def dimensions(m)
  if m.any? { |e| e.is_a?(Array) }
    d = m.group_by { |e| e.is_a?(Array) && dimensions(e) }.keys
    [m.size] + d.first if d.size == 1 && d.first
  else
    [m.size]
  end
end

dimensions(arr1)  #=> [3, 2, 2]
dimensions(arr2)  #=> nil

Explaination

The algorithm checks first for nested arrays, m.any? { |e| e.is_a?(Array) }. If there aren't nested arrays then you have just one dimension and it returns the size of the given array via [m.size] within the else block.

dimensions([1,2,3])  #=> [3]

If there is at least one nested array then you have to ensure that all elements are arrays and the arrays itself have the same dimensions. This check is done via d = m.group_by { |e| e.is_a?(Array) && dimensions(e) }.keys with groups all elements by its dimensions.

[[5, 6], [7, 8]].group_by { |e| ... }.keys
#=> [[2]], all nested array dimensions are equal [2]
[[1, 2], [4]].group_by { |e| ... }.keys
#=> [[1], [2]], different dimensions
[6, [7, 8]].group_by { |e| ... }.keys
#=> [false, [2]], an element isn't an array

The algorithm takes only the valid results of the group_by with if d.size == 1 && d.first and adds the dimensions of the nested arrays to the result via [m.size] + d.first. If there are more than one key element or only nil which means all nested arrays are invalid then it returns nil implicitly.

That's all.