Default values of hashes

G

Glenn

[Note: parts of this message were removed to make it a legal post.]

Hi,

Suppose I want to create a hash, which has a couple of elements with strings as the keys and the values of each of the elements were hashes themselves, and they each had a few elements, like this:

trt_hash = Hash.new( {} )

trt_hash['a'] = { :x => 0, :y => 0, :z => 0 }
trt_hash['b'] = { :x => 0, :y => 0, :z => 0 }

If I increment one of the values of the hash within the hash, like this, it works fine:

trt_hash['a'][:z] += 100
puts trt_hash['Hi'][:z] = 10
puts trt_hash.inspect

I get: {"a"=>{:z=>100, :x=>0, :y=>0}, "b"=>{:z=>0, :x=>0, :y=>0}}, which is what I expected.

But if I try to add another key to trt_hash and increment one of the keys of its hash, like this:

trt_hash['c'][:z] += 100
puts trt_hash.inspect

there is no error, but there is also no reference to the 'c' key when I print trt_hash.

Also, if I try this, I get an error:

trt_hash['a'][:w] += 100
puts trt_hash.inspect

The key above is 'a', which exists, but :w in the 'a' hash does not exist, so I get the following error:

undefined method `+' for nil:NilClass (NoMethodError)

I want to put able to add elements to trt_has at will and have them default to an empty hash, and I want to be able to modify the hash within the hash without predefining anything. I feel like this would work if I set up the hash and the hash within the hash with the proper defaults, but I do not know how to do that.

I thought if I defined trt_hash like this:

trt_hash = Hash.new( Hash.new(0))

I would be able to add any key to trt_hash and its value would have a default of an empty hash, and then I'd be able to add any element I wanted to the nested hash, and its default would be 0. But this does not seem to work.

Any suggestions?

Sorry if this email is unclear. If it is, please let me know and I will try to clarify.

Thanks!

Glenn
 
R

Rob Biedenharn

Directly from the rdoc:

"If a block is
specified, it will be called with the hash object and the key, and
should return the default value. It is the block's responsibility
to store the value in the hash if required."

So you want:

irb> trt_hash = Hash.new {|h,k| h[k] = Hash.new &h.default_proc }
=> {}
irb> trt_hash[:a][:b][:c] = "hi"
=> "hi"
irb> trt_hash
=> {:a=>{:b=>{:c=>"hi"}}}

-Rob

Rob Biedenharn http://agileconsultingllc.com
(e-mail address removed)
 
P

Peña, Botp

From: Glenn [mailto:[email protected]]=20
# I would be able to add any key to trt_hash and its value=20
# would have a default of an empty hash, and then I'd be able=20
# to add any element I wanted to the nested hash, and its=20
# default would be 0. But this does not seem to work.
# ...I thought if I defined trt_hash like this:
# trt_hash =3D Hash.new( Hash.new(0))

that is near to your objective. you just have to build the hash since =
defaults do not, ie use the block form to store value in hash =
http://www.ruby-doc.org/core/classes/Hash.src/M002868.html

try eg,
trt_hash =3D Hash.new{|h,k| h[k]=3D Hash.new{|h2,k2| h2[k2]=3D0} } =3D> {}
trt_hash["a"] =3D> {}
trt_hash =3D> {"a"=3D>{}}
trt_hash["a"][:x] =3D> 0
trt_hash =3D> {"a"=3D>{:x=3D>0}}
trt_hash["a"][:x]+=3D100 =3D> 100
trt_hash =3D> {"a"=3D>{:x=3D>100}}
trt_hash["a"][:x]+=3D100 =3D> 200
trt_hash =3D> {"a"=3D>{:x=3D>200}}
trt_hash["a"][:w]+=3D100 =3D> 100
trt_hash
=3D> {"a"=3D>{:w=3D>100, :x=3D>200}}
trt_hash["b"][:w]+=3D100 =3D> 100
trt_hash
=3D> {"a"=3D>{:w=3D>100, :x=3D>200}, "b"=3D>{:w=3D>100}}
 
G

Glenn

Both solutions are great. I had something else that was sort of working, b=
ut these are far more elegant.=0A=0AThanks!=0A=0A=0A=0A____________________=
____________=0AFrom: "Pe=F1a, Botp" <[email protected]>=0ATo: ruby-tal=
k ML <[email protected]>=0ASent: Saturday, January 3, 2009 12:03:35 A=
M=0ASubject: Re: Default values of hashes=0A=0AFrom: Glenn [mailto:glenn_ri=
(e-mail address removed)] =0A# I would be able to add any key to trt_hash and its value=
=0A# would have a default of an empty hash, and then I'd be able =0A# to a=
dd any element I wanted to the nested hash, and its =0A# default would be 0=
But this does not seem to work.=0A# ...I thought if I defined trt_hash l=
ike this:=0A# trt_hash =3D Hash.new( Hash.new(0))=0A=0Athat is near to you=
r objective. you just have to build the hash since defaults do not, ie use =
the block form to store value in hash http://www.ruby-doc.org/core/classes/=
Hash.src/M002868.html=0A=0Atry eg,=0A=0A> trt_hash =3D Hash.new{|h,k| h[k]=
=3D Hash.new{|h2,k2| h2[k2]=3D0} }=0A=3D> {}=0A> trt_hash["a"]=0A=3D> {}=0A=
trt_hash=0A=3D> {"a"=3D>{}}=0A> trt_hash["a"][:x]=0A=3D> 0=0A> trt_hash=
=0A=3D> {"a"=3D>{:x=3D>0}}=0A> trt_hash["a"][:x]+=3D100=0A=3D> 100=0A> trt_=
hash=0A=3D> {"a"=3D>{:x=3D>100}}=0A> trt_hash["a"][:x]+=3D100=0A=3D> 200=0A=
trt_hash=0A=3D> {"a"=3D>{:x=3D>200}}=0A> trt_hash["a"][:w]+=3D100=0A=3D> =
100=0A> trt_hash=0A=3D> {"a"=3D>{:w=3D>100, :x=3D>200}}=0A> trt_hash["b"][:=
w]+=3D100=0A=3D> 100=0A> trt_hash=0A=3D> {"a"=3D>{:w=3D>100, :x=3D>200}, "b=
"=3D>{:w=3D>100}}
 
G

Glenn

[Note: parts of this message were removed to make it a legal post.]

Thanks for the tips. I learned a lot from all of the suggestions.

Not sure, though, if I understand this line:

hash[1][2][3] = Hash.new(0)

Is that a valid line on its own. If so, it does not seem to work for me. I get the following error:

undefined method `[]=' for 0

Glenn



________________________________
From: David A. Black <[email protected]>
To: ruby-talk ML <[email protected]>
Sent: Monday, January 5, 2009 7:12:59 AM
Subject: Re: Default values of hashes

Hi --

Because a) that way you can't define a default value or proc for the innermost
hash, b) you won't get an error if you accidentally go one nesting too deep,
c) because that's what the op asked for and most importantly d) because you
already gave the answer with unlimited nesting ;-)

I'm with you on a) and d) :) I didn't understand the OP to be asking
for that constraint. As for a), you could just do the old-fashioned:

hash[1][2][3] = Hash.new(0)

and at least reap the benefits of getting the intermediate ones.


David

-- David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
 
J

Jesús Gabriel y Galán

Hi --
It's come up before; here's the solution I seem to recall:

hproc = lambda {|h,k| h[k] = Hash.new(&hproc) }
hash = Hash.new(&hproc)

hash[1][2][3] = 4
p hash # {1=>{2=>{3=>4}}}

Hi,

Another version, without the intermediate lambda:

irb(main):009:0> hash = Hash.new{|h,k| h[k] = Hash.new(&h.default_proc)}
=> {}
irb(main):010:0> hash[1][2][3] = 4
=> 4
irb(main):011:0> hash
=> {1=>{2=>{3=>4}}}

Jesus.
 
D

David A. Black

Hi --

Thanks for the tips. I learned a lot from all of the suggestions.

Not sure, though, if I understand this line:

hash[1][2][3] = Hash.new(0)

Is that a valid line on its own. If so, it does not seem to work for me. I get the following error:

undefined method `[]=' for 0

Here's how to use it:
hproc = lambda {|h,k| h[k] = Hash.new(&hproc) }
=> # said:
hash = Hash.new(&hproc) => {}
hash[1][2][3] = Hash.new(0) => {}
hash[1][2][3]["x"] += 10 => 10
hash[1][2][3]
=> {"x"=>10}


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
 
G

Glenn

[Note: parts of this message were removed to make it a legal post.]

Thanks, David. I think I understand it now.





________________________________
From: David A. Black <[email protected]>
To: ruby-talk ML <[email protected]>
Sent: Saturday, January 10, 2009 9:53:49 AM
Subject: Re: Default values of hashes

Hi --

Thanks for the tips. I learned a lot from all of the suggestions.

Not sure, though, if I understand this line:

hash[1][2][3] = Hash.new(0)

Is that a valid line on its own. If so, it does not seem to work for me. I get the following error:

undefined method `[]=' for 0

Here's how to use it:
hproc = lambda {|h,k| h[k] = Hash.new(&hproc) }
=> # said:
hash = Hash.new(&hproc) => {}
hash[1][2][3] = Hash.new(0) => {}
hash[1][2][3]["x"] += 10 => 10
hash[1][2][3]
=> {"x"=>10}


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top