1

I want to transform nested hashes into multidimensional arrays recursivley.

This is my current code:

def deep_to_a(hash)
  return  hash.to_a.each {|k, v|
    if k.is_a?(Hash) then
      hash[k.to_a] = hash.delete(k)
      deep_to_a(k)
    elsif v.is_a?(Hash) then
      hash[k] = hash[k].to_a
      if hash[k].any?{|k, v| k.is_a?(Hash) || v.is_a?(Hash)}
        deep_to_a(v)
      end
    end
  }
end

I want to get:

h = {11=>{12=>13, 14=>15}, 16=>17}
p deep_to_a(h) # => [[11, [[12, 13], [14, 15]]], [16, 17]] 

But I get

[[11, {12=>13, 14=>15}], [16, 17]]

How can I make it work?

Blackbam
  • 17,496
  • 26
  • 97
  • 150

2 Answers2

2

A destructive function is hard to debug. In this case, map is better than each + destructive assignment.

The result of hash.to_a is an array, so your iteration |k, v| is incorrect.

def deep_to_a(hash)
  hash.map do |v|
    if v.is_a?(Hash) or v.is_a?(Array) then
      deep_to_a(v)
    else
      v
    end
  end
end

h = {11=>{12=>13, 14=>15}, 16=>17}
p deep_to_a(h)
# [[11, [[12, 13], [14, 15]]], [16, 17]]
set0gut1
  • 1,652
  • 1
  • 8
  • 21
  • I was on the track, but this is great. – iGian May 17 '18 at 16:17
  • You don't need `return` either, as the return value from the block is the last value computed, so it is returned by the method. Note that you could instead (but not better) write, `case v; when Hash, Array; deep_to_a(v); else v; end`. Note `then` is optional with an `if` and generally not used. – Cary Swoveland May 19 '18 at 07:40
  • @CarySwoveland Certainly... I updated my post. Thankyou again! – set0gut1 May 19 '18 at 07:45
1
def deep_to_a(h)
  h.map { |k,v| [k, Hash === v ? deep_to_a(v) : v] }
end

deep_to_a({11=>{12=>13, 14=>15}, 16=>17})
  #=> [[11, [[12, 13], [14, 15]]], [16, 17]]
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100