Being very confused about this hash

Hi,

irb(main):039:0> hash = Hash.new []
=> {}
irb(main):040:0> hash['abc'] << 3
=> [3]
irb(main):041:0> hash.keys
=> []
irb(main):042:0> hash['def']
=> [3]

Since I have created a key/value pair with hash['abc'] << 3, why
hash.keys show nothing?
And, why hash['abc'] << 3 changed the hash's default value (which
should be an empty array)?

Thanks.

Ruby Newbee wrote:

irb(main):039:0> hash = Hash.new
=> {}
irb(main):040:0> hash['abc'] << 3
=> [3]
irb(main):041:0> hash.keys
=>
irb(main):042:0> hash['def']
=> [3]

Since I have created a key/value pair with hash['abc'] << 3, why
hash.keys show nothing?
And, why hash['abc'] << 3 changed the hash's default value (which
should be an empty array)?

I'm not sure why hash.keys was empty.

But with regard to the default value, we are providing an
object (a specific instance of the Array class) to be used
as the default.

We can illustrate this by asking Ruby to show us the object_id
of the default object:

irb(main):001:0> ary =
=>
irb(main):002:0> ary.object_id
=> 20919800
irb(main):003:0> h = Hash.new ary
=> {}
irb(main):004:0> h['foo'].object_id
=> 20919800
irb(main):005:0> h['bar'].object_id
=> 20919800

So we can see the object_id of the default object is indeed
the same object_id of the array we provided to Hash.new.

  * * *

We can instead provide a block to Hash.new which allows us
to create a _new_ unique object (instance of Array) whenever
a new default value is needed:

irb(main):001:0> h = Hash.new {|h,k| h[k] = }
=> {}
irb(main):002:0> h['abc'] << 3
=> [3]
irb(main):003:0> h.keys
=> ["abc"]
irb(main):004:0> h['def']
=>

Regards,

Bill

irb(main):039:0> hash = Hash.new

I assume here that is the default value, right?

=> {}
irb(main):040:0> hash['abc'] << 3
=> [3]
irb(main):041:0> hash.keys
=>
irb(main):042:0> hash['def']
=> [3]

Since I have created a key/value pair with hash['abc'] << 3, why
hash.keys show nothing?

Because you haven't set anything in the hash. That is,

hash['abc'] = 3

is equivalent to:

hash.=('abc', 3)

On the other hand, what you've done is:

hash['abc'] << 3

That's equivalent to:

hash.('abc') << 3

I don't know if that helps, but that's the difference. Let me put it this way
-- suppose you hadn't changed the default value:

irb(main):001:0> hash = {}
=> {}
irb(main):002:0> hash['abc']
=> nil
irb(main):003:0> hash.keys
=>

Do you understand why that works the way it does? But there's nothing special
about nil here, it's just the, erm, _default_ default value -- it's what Hash
uses as a default value if you don't specify one.

And, why hash['abc'] << 3 changed the hash's default value (which
should be an empty array)?

Because you set the default value to , which creates an empty array object,
once. The hash is just assigning it each time. Think of it this way:

irb(main):004:0> default =
=>
irb(main):005:0> a = default
=>
irb(main):006:0> b = default
=>
irb(main):007:0> a << 3
=> [3]
irb(main):008:0> b
=> [3]

What you really want is the block notation of creating a hash, which lets you
actually specify some code that's run to create a default value, as Bill Kelly
says. The reason his code works is that when you try to access a value that
doesn't exist, it actually runs some code which sets that value:

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

It wouldn't work at all if you just did this:

h = Hash.new {|h,k| }

That would at least give you a new array each time, but since it wouldn't set
it, you'd see weird things:

irb(main):010:0> h['abc'] << 3
=> [3]
irb(main):011:0> h['abc']
=>
irb(main):012:0> h.keys
=>

···

On Tuesday 05 January 2010 09:39:53 pm Ruby Newbee wrote:

I believe that keys is returning empty because the hash is empty. If I remember correctly, the default hash element mechanism doesn't create the element, just returns the default element when referenced.

So hash['abc'] returns our specific default instance which then gets a 3 added to it. That's why all future defaults get the [3]...its the identical instance. But no actually elements are added to the hash.

···

On Jan 5, 2010, at 11:02 PM, Bill Kelly wrote:

Ruby Newbee wrote:

irb(main):039:0> hash = Hash.new
=> {}
irb(main):040:0> hash['abc'] << 3
=> [3]
irb(main):041:0> hash.keys
=>
irb(main):042:0> hash['def']
=> [3]

Since I have created a key/value pair with hash['abc'] << 3, why
hash.keys show nothing?
And, why hash['abc'] << 3 changed the hash's default value (which
should be an empty array)?

I'm not sure why hash.keys was empty.

But with regard to the default value, we are providing an
object (a specific instance of the Array class) to be used
as the default.

We can illustrate this by asking Ruby to show us the object_id
of the default object:

irb(main):001:0> ary =
=>
irb(main):002:0> ary.object_id
=> 20919800
irb(main):003:0> h = Hash.new ary
=> {}
irb(main):004:0> h['foo'].object_id
=> 20919800
irb(main):005:0> h['bar'].object_id
=> 20919800

So we can see the object_id of the default object is indeed
the same object_id of the array we provided to Hash.new.

* * *

We can instead provide a block to Hash.new which allows us
to create a _new_ unique object (instance of Array) whenever
a new default value is needed:

irb(main):001:0> h = Hash.new {|h,k| h[k] = }
=> {}
irb(main):002:0> h['abc'] << 3
=> [3]
irb(main):003:0> h.keys
=> ["abc"]
irb(main):004:0> h['def']
=>

Regards,

Bill

Thanks for the explaining, now I think I have got it.
I'm always thinking it as the Perl way, since in Perl the syntax like
hash['abc'] << 3 will create a key/value pairs.

perl -MData::Dumper -le '$hash={}; push @{$hash->{'abc'}},3; print Dumper $hash'
$VAR1 = {
          'abc' => [
                     3
                   ]
        };

I didn't know in Ruby hash['abc'] = 3 and hash['abc'] << 3 are different.
Thanks again.

···

On Wed, Jan 6, 2010 at 1:52 PM, David Masover <ninja@slaphack.com> wrote:

On Tuesday 05 January 2010 09:39:53 pm Ruby Newbee wrote:

Since I have created a key/value pair with hash['abc'] << 3, why
hash.keys show nothing?

Because you haven't set anything in the hash. That is,

hash['abc'] = 3

is equivalent to:

hash.=('abc', 3)

On the other hand, what you've done is:

hash['abc'] << 3

That's equivalent to:

hash.('abc') << 3