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.

On Tue, Mar 9, 2010 at 8:31 PM, Glenn R. [email protected] 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

Jean G. wrote:

On Tue, Mar 9, 2010 at 8:31 PM, Glenn R. [email protected] 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}}}}

Glenn R. 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] }

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

I coded up a way to do this in a method without needing to monkey-patch

Hash: http://forr.st/~zaG

#
Takes in a hash in input and merges it with an output hash to produce

a hash that is only one dimensional

#
i.e. there are no nested elements in the hash

def make_hash_one_dimensional(input = {}, output = {}, options = {})

input.each do |key, value|

key = options[:prefix].nil? ? “#{key}” :

“#{options[:prefix]}#{options[:delimiter]||”*“}#{key}”*

if value.is_a? Hash

make_hash_one_dimensional(value, output, :prefix => key,

:delimiter => "")

else

output[key] = value

end

end

output

end

hash = {:a => {:b => {:c => {:d => {:e => “hi”}}}}, :f => “there”}

make_hash_one_dimensional(hash)

#=> {“f”=>“there”, “a_b_c_d_e”=>“hi”}

From there it would be easy to turn this into an array. Enjoy

–

@schneems

2010/3/9 Glenn R. [email protected]:

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

array << [”#{k1}: #{k2}: #{k3}:”,hash[k1][k2][k3]]

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

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