Hash default -- what's going on here?

I’ve hit what is either an egregious bug in ruby (1.8.4) or some
subtlety
that I haven’t grasped. [Most likely the latter… (:-)]

Briefly I had two arrays of symbol strings of which I wanted to find
which symbols occurred in both arrays, so I thought I’d use a hash with
the key being the symbol and the value a two element array that would
be used to flag which set(s) the symbol was found in. The following
is a simplified (and ‘instrumented’) version of what I was trying to
do:

listA=[“line1”,“line2”,“line3”]
listB=[“line5”,“line3”,“line4”]
table={}
table.default=[0,0]
print "table default originally: ", table.default, “\n”

listA.each {|a| t=table[a]; print a," – undef value: “,t;
t[0]=1; print " new value: “,t,”\n”;
table[a]=t}
print "table default now: ", table.default, “\n”

listB.each {|a| t=table[a]; print a," – undef value: “,t;
t[1]=1; print " new value: “,t,”\n”;
table[a]=t}

print "table default now: ", table.default, “\ntable contents:\n”
table.each {|k,v| print k, ": ", v[0], v[1], “\n”}

Now, in the ‘each’ statements, the variable ‘a’ should (except in one
case)
not be in the hash yet, so I would expect the original provided default
to
be returned. However, here’s the printout I get:

table default now: 00
line1 – undef value: 00 new value: 10
line2 – undef value: 10 new value: 10
line3 – undef value: 10 new value: 10
table default now: 10
line5 – undef value: 10 new value: 11
line3 – undef value: 11 new value: 11
line4 – undef value: 11 new value: 11
table default now: 11
table contents:
line1: 11
line2: 11
line3: 11
line4: 11
line5: 11

Dunhh? Why is the default value getting changed? I can’t see that
I’m resetting it anywhere. If I do the same sort of manipulations
outside of a block (one at a time in irb for instance) things happen
as I would expect – the default remains as set, and is returned for
any undefined key.

Illumination appreciated.
– Pete –

I’m guessing it’s got something to do with your assignment

                    table[a]=t

the array that is the hash’s default can be assigned to. I think that’s
what you’re doing. If i did some testing I might be more sure :slight_smile:

;Daniel

On 15/03/06, Pete G. [email protected] wrote:

do:
print "table default now: ", table.default, “\n”
case)
line4 – undef value: 11 new value: 11
I’m resetting it anywhere. If I do the same sort of manipulations
outside of a block (one at a time in irb for instance) things happen
as I would expect – the default remains as set, and is returned for
any undefined key.

Illumination appreciated.
– Pete –


Daniel B.
http://danielbaird.com (TiddlyW;nks! :: Whiteboard Koala :: Blog ::
Things
That Suck)
[[My webhost uptime is ~ 92%… if no answer pls call again later!]]

In article [email protected],
Daniel B. [email protected] wrote:

what you’re doing. If i did some testing I might be more sure :slight_smile:

Heh – you can tell I’m new to ruby… (:-))

An hour after I posted I suddenly realized what is going on.
When you assign an array, the array is not copied! the assigned-to
variable just gets a pointer to the other array.

So when I did “t=table[a]” I was setting t to a pointer to the default!
When I changed t[n], of course the default was actually what got
changed…

Sigh. Hardly a novel gotcha, but I fell right into it.

Not sure of the best way to do what I want – maybe just use ‘has_key?’
and then do the right thing.
– Pete –

“Pete G.” [email protected] wrote in message
news:[email protected]

Not sure of the best way to do what I want – maybe just use ‘has_key?’
and then do the right thing.

The question is what exactly are you trying to do?

robert

2006/3/15, Pete G. [email protected]:

listA=[“line1”,“line2”,“line3”]
listB=[“line5”,“line3”,“line4”]
table={}

Here’s the problem:

table.default=[0,0]

What you are looking for is:

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

otherwise you are always pointing to the same object