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.
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]]]]