Flatten out Hash

Hi,

I would like to take a nested hash that looks like this:

{"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=>2}}}

and turn it into an array of 2 element arrays like this:

[["a: b2: c2: ", 2], ["a: b: c: ", 1]]

Is there a simple way to do this? I wrote a method that iterates
through the nested hashes recursively, but it's a bit cumbersome.

···

--
Posted via http://www.ruby-forum.com/.

My method:

hash = {"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=>2}}}
array =

hash.keys.each do |k1|
    hash[k1].keys.each do |k2|
        hash[k1][k2].keys.each do |k3|
             array << ["#{k1}: #{k2}: #{k3}:",hash[k1][k2][k3]]
        end
    end
end

···

On Tue, Mar 9, 2010 at 8:31 PM, Glenn Ritz <glenn_ritz@yahoo.com> wrote:

Hi,

I would like to take a nested hash that looks like this:

{"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=>2}}}

and turn it into an array of 2 element arrays like this:

[["a: b2: c2: ", 2], ["a: b: c: ", 1]]

Glenn Ritz wrote:

Hi,

I would like to take a nested hash that looks like this:

{"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=>2}}}

and turn it into an array of 2 element arrays like this:

[["a: b2: c2: ", 2], ["a: b: c: ", 1]]

Is there a simple way to do this? I wrote a method that iterates
through the nested hashes recursively, but it's a bit cumbersome.

I think that's the right way, if the nesting depth is variable. One
example:

class Hash
  def flat_each(prefix="", &blk)
    each do |k,v|
      if v.is_a?(Hash)
        v.flat_each(prefix+k+": ", &blk)
      else
        yield prefix+k, v
      end
    end
  end
end

require 'enumerator'
h = {"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=>2}}}
p h.to_enum(:flat_each).collect { |k,v| [k,v] }

···

--
Posted via http://www.ruby-forum.com/\.

Jean G. wrote:

···

On Tue, Mar 9, 2010 at 8:31 PM, Glenn Ritz <glenn_ritz@yahoo.com> wrote:

Hi,

I would like to take a nested hash that looks like this:

{"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=>2}}}

and turn it into an array of 2 element arrays like this:

[["a: b2: c2: ", 2], ["a: b: c: ", 1]]

My method:

hash = {"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=>2}}}
array =

hash.keys.each do |k1|
    hash[k1].keys.each do |k2|
        hash[k1][k2].keys.each do |k3|
             array << ["#{k1}: #{k2}: #{k3}:",hash[k1][k2][k3]]
        end
    end
end

Thanks, Jean.

It's a good solution. But it's dependent on there being 3 levels of
hashes. What if you didn't know how many levels of nesting there was
before executing the code? Suppose the hash looked like this instead:

{"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=> {"d2" => 2}}}}

--
Posted via http://www.ruby-forum.com/\.

Perhaps a better solution is to yield an array of keys (being the 'path'
to reach the end node), because then the caller can choose how to
combine them.

class Hash
  def flat_each(prefix=[], &blk)
    each do |k,v|
      if v.is_a?(Hash)
        v.flat_each(prefix+[k], &blk)
      else
        yield prefix+[k], v
      end
    end
  end
end

require 'enumerator'
h = {"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=>2}}}
h = {"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=> {"d2" => 2}}}}
p h.to_enum(:flat_each).collect { |k,v| [k.join(": "),v] }
h.flat_each do |k,v|
  puts "#{k.join("/")} => #{v}"
end

···

--
Posted via http://www.ruby-forum.com/.

Basically you want to do a DFS and store the path to the root for
every leaf. Something like DFS in nested Hashes · GitHub

Kind regards

robert

···

2010/3/9 Glenn Ritz <glenn_ritz@yahoo.com>:

Jean G. wrote:

On Tue, Mar 9, 2010 at 8:31 PM, Glenn Ritz <glenn_ritz@yahoo.com> wrote:

Hi,

I would like to take a nested hash that looks like this:

{"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=>2}}}

and turn it into an array of 2 element arrays like this:

[["a: b2: c2: ", 2], ["a: b: c: ", 1]]

My method:

hash = {"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=>2}}}
array =

hash.keys.each do |k1|
hash[k1].keys.each do |k2|
hash[k1][k2].keys.each do |k3|
array << ["#{k1}: #{k2}: #{k3}:",hash[k1][k2][k3]]
end
end
end

Thanks, Jean.

It's a good solution. But it's dependent on there being 3 levels of
hashes. What if you didn't know how many levels of nesting there was
before executing the code? Suppose the hash looked like this instead:

{"a"=>{"b"=>{"c"=>1}, "b2"=>{"c2"=> {"d2" => 2}}}}

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/