Nested hash from array of paths

Hi,

I’m banging my head against the wall with the following problem:

array = [“home”, “about”, “about/history”, “about/company”,
“about/history/part1”, “about/history/part2”]

I want to build a tree from this array in the form of a nested hash:

{“home” => nil, "about => {“history => [“part1” => nil, “part2” =>
nil]”, “company” => nil}}

Any ideas as to how best to solve this?

Regards

Adam

On 2/11/08, Adam G. [email protected] wrote:

I could only figure out how to do it using 2 passes:

tree ={}
array.sort.each{|w|
h=tree
w.split(‘/’).each{|part| h=h[part]||=Hash.new}
}

def cleanup h
return true if h.empty?
h.find_all{|k,v|h[k]=nil if v.is_a?(Hash)&&cleanup(v)}
false
end

cleanup tree
p tree
=> {“about”=>{“company”=>nil, “history”=>{“part1”=>nil,
“part2”=>nil}}, “home”=>nil}

-Adam

On Feb 11, 4:43 pm, Adam G. [email protected] wrote:

nil]", “company” => nil}}
Your output doesn’t seem consistent. (Why an array sometimes but a
hash others?)
Here’s something simple and close:

array = [“home”, “about”, “about/history”, “about/company”, “about/
history/part1”, “about/history/part2”]

auto_hash = Hash.new{ |h,k| h[k] = Hash.new &h.default_proc }

array.each{ |path|
sub = auto_hash
path.split( “/” ).each{ |dir| sub[dir]; sub = sub[dir] }
}
p auto_hash
#=> {“about”=>{“company”=>{}, “history”=>{“part1”=>{}, “part2”=>{}}},
“home”=>{}}

On Feb 11, 10:00 pm, Phrogz [email protected] wrote:

array.each{ |path|
sub = auto_hash
path.split( “/” ).each{ |dir| sub[dir]; sub = sub[dir] }
}

Bah, of course that can be written as:

array.each{ |path|
sub = auto_hash
path.split( “/” ).each{ |dir| sub = sub[dir] }
}

Gavin K. wrote:

On Feb 11, 10:00�pm, Phrogz [email protected] wrote:

array.each{ |path|
� sub = auto_hash
� path.split( “/” ).each{ |dir| sub[dir]; sub = sub[dir] }
}

Bah, of course that can be written as:

array.each{ |path|
sub = auto_hash
path.split( “/” ).each{ |dir| sub = sub[dir] }
}

Gavin,

thanks mate - works a charm. I’m glad you spotted my deliberately
inconsistent output as well! :wink:

Adam

2008/2/12, Phrogz [email protected]:

path.split( “/” ).each{ |dir| sub = sub[dir] }
}

Or even

array.each{ |path|
path.to_enum(:split, “/” ).inject(auto_hash){ |sub, dir| sub[dir] }
}

Full credits go to you for this line alone:

auto_hash = Hash.new{ |h,k| h[k] = Hash.new &h.default_proc }

Never occurred to me to use this nice one line approach instead of the
usual two line version (first create block then use it). Thanks a
lot, this is really a gem!

Kind regards

robert

On Feb 12, 7:12 am, Robert K. [email protected] wrote:

Full credits go to you for this line alone:

auto_hash = Hash.new{ |h,k| h[k] = Hash.new &h.default_proc }

Never occurred to me to use this nice one line approach instead of the
usual two line version (first create block then use it). Thanks a
lot, this is really a gem!

I wish I could claim those credits. I had always used the two-line
version two until Jens W. posted this gem in [ruby-talk:288946]

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/288946

Alternatively:

require ‘xkeys’
array = [“home”, “about”, “about/history”, “about/company”,
“about/history/part1”, “about/history/part2”]
tree = {}.extend XKeys::Hash
array.each { |path| tree[*(path.split ?/)] = nil }
p tree

{“home”=>nil, “about”=>{“history”=>{“part1”=>nil, “part2”=>nil},

“company”=>nil}}

The leaves are nil (not empty hashes), and the code is slightly less
convoluted.

On 12.02.2008 15:30, Phrogz wrote:

version two until Jens W. posted this gem in [ruby-talk:288946]

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/288946

Ok, then you get at least part of the credits for bringing it up again.
:wink:

Kind regards

robert