Forum: Ruby problem appending to arrays in a hash of arrays

Announcement (2017-05-07): is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see and for other Rails- und Ruby-related community platforms.
Mike S. (Guest)
on 2007-05-23 06:46
(Received via mailing list)
I'm trying to create a hash with each element being an array (initially
empty), and I've tried a few variations to get what I want. Why don't
last 2 lines work?

h = ( )
h["abc"] = h["abc"] + [ "foo" ]   # this works
h["abc"] << "foo"                 # this doesn't
h["abc"].push ( "foo" )           # this doesn't either

(Note: the above code isn't the actual code I'm using, but a simplified

Mike S.
Chris C. (Guest)
on 2007-05-23 06:52
(Received via mailing list)
On 5/22/07, Mike S. <removed_email_address@domain.invalid> wrote:
> version.)
> Mike S.

>> g = {|h,k| h[k]=[]}
=> {}
>> g["abc"] << :foo
=> [:foo]
>> g
=> {"abc"=>[:foo]}
Rick D. (Guest)
on 2007-05-23 16:50
(Received via mailing list)
On 5/22/07, Chris C. <removed_email_address@domain.invalid> wrote:
> > (Note: the above code isn't the actual code I'm using, but a simplified
> > version.)

> >> g = {|h,k| h[k]=[]}
> => {}
> >> g["abc"] << :foo
> => [:foo]
> >> g
> => {"abc"=>[:foo]}

That's a fish for Mike, here's a little fishing lesson.

Mike, you didn't say how it didn't work, there are actually two
related problems.

The Hash creation technique:

creates a hash which returns the value of expression as the default
value when a key doesn't exist in the hash.  Normally hashes return
nil for an unknown key.

The expression is evaluated before calling the new method, so in your

   h =

creates an empty array for the default value, this instance of array
will be returned for any key which isn't in the hash.  Now:

1:    h[:a] += [:c] # this replaces h[:a]
2:    h[:b] << :d   # this appends :d to what is returned by h[:b]
3:    h[:b] # returns [:d] which looks like it worked BUT
4:    h[:c]  # also returns [:d] ??????

What happened is that the hash simply returns that default value,
which is the same object every time.  That's problem #1, line 2
actually changes the state of the default value.  Now:

5:   h[:d] << :f  # returns [:d, :f] and so does
6:   h[:b]            # ??? and
7:   h[:c]            # ???

This is problem two, accessing the hash via a non-existent key doesn't
change the hash, it simply returns the default value.

8.  h.inspect     # returns "{:a=>[:c]}"

Now Chris's solution uses without a parameter but with a block

      h  = {|h, k| h[k] = []}

which makes a hash which, if accessed with an unknown key, evaluates
the block passing the hash itself, and the key as parameters, and
returns the value of the block.  Chris's block actually stores an
empty array in the hash at that key.  And since the block is evaluated
at the time the hash is accessed rather than when it is created, each
key will get it's own instance of an empty array. Which solves both

Note that the block given to can do whatever it wants to with
the key and hash.  Chris's block stored the value, which is generally
done, but it doesn't have to.  For example if the Hash were created

     h = {[]}

we'd get a hash which returned a newly instantiated empty array for an
unknown key, but didn't store it.  This wouldn't work for what Mike's
trying to do:

    h[:a] << :b  # returns [:b]  but then
    h[:a]             # returns []

This solves problem #1, but not problem #2.   This technique might be
useful in other situations though.

Rick DeNatale

My blog on Ruby
Mike S. (Guest)
on 2007-05-23 19:28
(Received via mailing list)
I sort of understand your explanation, but what are :a, :b, etc.?

Mike S.
Rick D. (Guest)
on 2007-05-23 21:41
(Received via mailing list)
On 5/23/07, Mike S. <removed_email_address@domain.invalid> wrote:
> I sort of understand your explanation, but what are :a, :b, etc.?

They're just symbols, strings or most other objects would work just as
well as keys, values could be anything.

Rick DeNatale

My blog on Ruby
This topic is locked and can not be replied to.