[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Re: Tricky: converting path into a Hash

Joel VanderWerf

1/22/2007 1:46:00 AM

Robert MannI wrote:
> Hello!
>
>
> I am wondering if the mighty ruby crowd has a brilliant idea for a tricky
> problem I am solving.
>
> I need to store a path as a tree in a hash.
>
> Given:
> a/b/c
>
> I want the Hash:
> { 'a' => { 'b' => { 'c' => { } } } }
> or written differently
> hsh['a']['b']['c'] = {}

This is a fairly standard idiom:

pr = proc {|h,k| h[k] = Hash.new(&pr)}

h = Hash.new(&pr)

h['a']['b']['c'] = {}

p h # ==> {"a"=>{"b"=>{"c"=>{}}}}

For more discussion, search for "autovivify hash" on the list...

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

3 Answers

Ken Bloom

1/22/2007 4:08:00 AM

0

On Mon, 22 Jan 2007 10:46:00 +0900, Joel VanderWerf wrote:

> Robert MannI wrote:
>> Hello!
>>
>>
>> I am wondering if the mighty ruby crowd has a brilliant idea for a tricky
>> problem I am solving.
>>
>> I need to store a path as a tree in a hash.
>>
>> Given:
>> a/b/c
>>
>> I want the Hash:
>> { 'a' => { 'b' => { 'c' => { } } } }
>> or written differently
>> hsh['a']['b']['c'] = {}
>
> This is a fairly standard idiom:
>
> pr = proc {|h,k| h[k] = Hash.new(&pr)}
>
> h = Hash.new(&pr)
>
> h['a']['b']['c'] = {}
>
> p h # ==> {"a"=>{"b"=>{"c"=>{}}}}
>
> For more discussion, search for "autovivify hash" on the list...
>

Then the inject trick becomes

pr = lambda {|h,k| h[k] = Hash.new(&pr)}
z=Hash.new(&pr)
s = "a/b/c"
s.split("/").inject(z){|ha,co| ha[co]}
s = "a/b/d"
s.split("/").inject(z){|ha,co| ha[co]}
s = "a/d/e"
s.split("/").inject(z){|ha,co| ha[co]}
s = "c/a/t"
s.split("/").inject(z){|ha,co| ha[co]}
p z

For some reason you can't name the hash h, otherwise the parameter
assignment in the proc will override it. Why is that, and is there any way
to avoid it?

This is what you really wanted because you wanted a really easy way to
merge all of these paths together into one hash, whereas if you
constructed the hashes separately then merged them you'd be losing data.
Try the following to see what I mean.

h1={'a'=>{'b'=>{'c'=>{}}}}
h2={'a'=>{'b'=>{'d'=>{}}}}
h3={'a'=>{'c'=>{'e'=>{}}}}
h4={'c'=>{'a'=>{'t'=>{}}}}
p h1.merge(h2).merge(h3).merge(h4)

All of the a/b branches are overwritten when 'a'=>{'c'...} is merged over
it. (And the second 'a'=>{'b'...} hash overwrites the first completely).

--Ken Bloom

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu...

Logan Capaldo

1/22/2007 4:33:00 AM

0

On Mon, Jan 22, 2007 at 01:10:08PM +0900, Ken Bloom wrote:
>
> Then the inject trick becomes
>
> pr = lambda {|h,k| h[k] = Hash.new(&pr)}
> z=Hash.new(&pr)
> s = "a/b/c"
> s.split("/").inject(z){|ha,co| ha[co]}
> s = "a/b/d"
> s.split("/").inject(z){|ha,co| ha[co]}
> s = "a/d/e"
> s.split("/").inject(z){|ha,co| ha[co]}
> s = "c/a/t"
> s.split("/").inject(z){|ha,co| ha[co]}
> p z
>
> For some reason you can't name the hash h, otherwise the parameter
> assignment in the proc will override it. Why is that, and is there any way
> to avoid it?
Same reason as:

h = 3
lambda { h = "three" }.call
p h # prints "three"

Block arguments act like assignment. This can be seen in pathological
examples like lambda { |$a_global| ... } and lambda { |@an_ivar| ... }



Ara.T.Howard

1/22/2007 4:38:00 AM

0