Flatten out Hash

G

Glenn Ritz

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

Jean G.

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
 
G

Glenn Ritz

Jean said:
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}}}}
 
B

Brian Candler

Glenn said:
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] }
 
B

Brian Candler

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
 
R

Robert Klemme

2010/3/9 Glenn Ritz said:
Jean said:
Hi,

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

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

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

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

My method:

hash =3D {"a"=3D>{"b"=3D>{"c"=3D>1}, "b2"=3D>{"c2"=3D>2}}}
array =3D []

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

Thanks, Jean.

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

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

Basically you want to do a DFS and store the path to the root for
every leaf. Something like http://gist.github.com/326558

Kind regards

robert

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top