Search code examples
arraysrubyalgorithmmultidimensional-arrayruby-hash

Turn nested hash to two dimensional array in Ruby


I want to write a method that can receive a nested hash and return a nested array of two dimensional arrays.

hash_to_a({1=>2, 2=>3, {3=>4, 5=>6}=>7})  # [[1, 2], [2, 3], [[[3, 4], [5, 6]], 7]]
hash_to_a({{5=>{1=>3, 2=>4}}=>{7=>8}})    # [[[[5, [[1, 3], [2, 4]]]], [[7, 8]]]]
hash_to_a({5=>{1=>3, 2=>4}})              # [[5, [[1, 3], [2, 4]]]]

So far i've got this:

def hash_to_a(a_hash)
  result = []
  a_hash.each { |k, v|
    if k.is_a?(Hash) 
      result << k.to_a
    else
      result << k
    end
    if v.is_a?(Hash)
      result << v.to_a
    else
      result << v
    end
  }
  result
end

The results are of course not desirable

hash_to_a({1=>2, 2=>3, {3=>4, 5=>6}=>7})  # [1, 2, 2, 3, [[3, 4], [5, 6]], 7]
hash_to_a({{5=>{1=>3, 2=>4}}=>{7=>8}})    # [[[5, {1=>3, 2=>4}]], [[7, 8]]]
hash_to_a({5=>{1=>3, 2=>4}})              # [5, [[1, 3], [2, 4]]]

How can I accomplish the wanted results? I also tried recusion but just can't wrap my mind around this problem.


Solution

  • Start with your simplest case, with no nesting.

    def hash_to_a(hash)  # { 3 => 4 }
      hash.map do |k, v| # 3, 4
        [k, v]           # [3, 4] 
      end
    end
    

    Now the problem is that for the more complicated cases we don't want to return the key or value if it's a hash, we want to convert it from a hash first.

    def hash_to_a(hash)
      hash.map do |k, v|
        [hash_to_a(k), hash_to_a(v)]
      end
    end
    

    This will blow up with our simple case because 3 does not have a method #map. This is because we're not handling the base case yet. Just takes a line of code to keep from trying to map things that aren't hashes. We want it to behave just like our first try: do nothing but return the key or the value.

    def hash_to_a(object)
      return object unless object.is_a? Hash
      object.map do |k, v|
        [hash_to_a(k), hash_to_a(v)]
      end
    end
    

    And we're done.

    hash_to_a({1=>2, 2=>3, {3=>4, 5=>6}=>7})  # [[1, 2], [2, 3], [[[3, 4], [5, 6]], 7]]
    hash_to_a({{5=>{1=>3, 2=>4}}=>{7=>8}})    # [[[[5, [[1, 3], [2, 4]]]], [[7, 8]]]]
    hash_to_a({5=>{1=>3, 2=>4}})              # [[5, [[1, 3], [2, 4]]]]