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 N. wrote:

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

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

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

I assume here that [] is the default value, right?

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. << 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 Wed, Jan 6, 2010 at 1:52 PM, David M. [email protected]
wrote:

hash.[]=(‘abc’, 3)

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

hash[‘abc’] << 3

That’s equivalent to:

hash. << 3

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.

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.