1
\$\begingroup\$

I have an array that might be either one-dimensional, two-dimension, or three-dimensional. I'm wanting to check to see if all values in a given direction are constant.

Here are 3 functions:

# Check to see if all values in the first dimension are equal                                                                                                                                             
def constant1D(arr)
  arr.inject(true) { |same, val| same and val == arr[0] }
end

# Check to see if all values in the second dimension are equal                                                                                                                                            
def constant2Dy(arr)
  arr.inject(true) { |same, x| same and constant1D(x) }
end

# Check to see if all values in the third dimension are equal                                                                                                                                             
def constant3Dz(arr)
  arr.inject(true) { |same, x| same and constant2Dy(x) }
end

And here is the code that would be checking:

constantX = constant1D(values)
constantY = ((mapType == :twoD) and constant2Dy(values))
constantZ = ((mapType == :threeD) and constant3Dz(values))

Here are some sample runs:

threeDa = [[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
threeDb = [[[1, 2, 3], [1, 2, 3], [1, 2, 3]], [[4, 5, 6], [4, 5, 6], [4, 5, 6]], [[7, 8, 9], [7, 8, 9], [7, 8, 9]]]
threeDc = [[[1, 1, 1], [2, 2, 2], [3, 3, 3]], [[4, 4, 4], [5, 5, 5], [6, 6, 6]], [[7, 7, 7], [8, 8, 8], [9, 9, 9]]]
>> constant1D(threeDa)
=> true
>> constant1D(threeDb)
=> false
>> constant1D(threeDc)
=> false
>> constant2Dy(threeDa)
=> false
>> constant2Dy(threeDb)
=> true
>> constant2Dy(threeDc)
=> false
>> constant3Dz(threeDa)
=> false
>> constant3Dz(threeDb)
=> false
>> constant3Dz(threeDc)
=> true

Is this the most "Ruby" way, or is there a better way? E.g., referencing arr[0] in the above has a little bit of code smell to me, but perhaps I'm overthinking it.

\$\endgroup\$
5
\$\begingroup\$

[Rewritten]

You want to avoid inject, as uniq.size is O(n) and clearer. Monkey patching array makes the code clearer and is idiomatic Ruby. Methods that return a boolean should end in ?. Method names should be snake_case. Here you go:

class Array
  # Check to see if all values in the first dimension are equal
  def constant_1d?
    uniq.size == 1
  end

  # Check to see if all values in the second dimension are equal
  def constant_2dy?
    all?(&:constant_1d?)
  end

  # Check to see if all values in the third dimension are equal
  def constant_3dz?
    all?(&:constant_2dy?)
  end
end
\$\endgroup\$
5
  • 1
    \$\begingroup\$ O(N) complexity, good. \$\endgroup\$
    – Caridorc
    Sep 11 '15 at 17:50
  • \$\begingroup\$ I've verified that this works fine (with == 1 instead of .one?) as a replacement for constant1D, but does the inject seem bad for the other 2D and 3D checks? For constant2Dy, I've verified that I can do arr.transpose.uniq.size == 1, but am still pondering constant3Dz. \$\endgroup\$ Sep 11 '15 at 18:01
  • \$\begingroup\$ @Ben Hocking, what do you think? \$\endgroup\$
    – Dan Kohn
    Sep 12 '15 at 15:56
  • \$\begingroup\$ each is used to perform side-effects, it returns the array being iterated, I don't see how this can be working. \$\endgroup\$
    – tokland
    Sep 12 '15 at 19:42
  • \$\begingroup\$ @dankohn, tested it out with my arrays and it works fine. It's always nice to try thinking in different ways. Thanks! \$\endgroup\$ Sep 14 '15 at 11:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.