# Strange Hash default behaviour

**URL:** <https://rubytalk.org/t/strange-hash-default-behaviour/2211>\
**Category:** ruby-talk\
**Created:** [9 October 2002 10:31 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211 "2002-10-09T10:31:25Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![John\_Tromp2](https://avatars.discourse-cdn.com/v4/letter/j/7ba0ec/32.png) [@John\_Tromp2](https://rubytalk.org/u/John_Tromp2)\
**Post date:** [9 October 2002 10:31 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/1 "2002-10-09T10:31:25Z")

</div>

I wrote a ruby program to read a list of graph edges and produce  
an adjacency list using a Hash. The graph edges look like  
p12 – p31  
I hoped to write this as

nbs = Hash.new([])  
while line = gets  
if line =~ /p(\d+) – p(\d+)/  
u, v = $1.to\_i, $2.to\_i  
nbs[u] \<\< v  
nbs[v] \<\< u  
end  
end

but unfortunately, the default value [] for the Hash is shared by all  
elements, rather than copied, as I expected.

I was wondering if this was a conscious design decision, or an oversight.  
I finally ended up with the much uglier

nbs = Hash.new()  
while line = gets  
if line =~ /p(\d+) – p(\d+)/  
u, v = $1.to\_i, $2.to\_i  
nbs[u] = [] unless nbs.has\_key?(u)  
nbs[u] \<\< v  
nbs[v] = [] unless nbs.has\_key?(v)  
nbs[v] \<\< u  
end  
end

Is there another way to avoid the explicit tests?

regards,

%!PS % -John Tromp ([http://www.cwi.nl/~tromp/](http://www.cwi.nl/~tromp/))  
42 42 scale 7 9 translate .07 setlinewidth .5 setgray/c{arc clip fill  
setgray}def 1 0 0 42 1 0 c 0 1 1{0 3 3 90 270 arc 0 0 6 0 -3 3 90 270  
arcn 270 90 c -2 2 4{-6 moveto 0 12 rlineto}for -5 2 5{-3 exch moveto  
9 0 rlineto}for stroke 0 0 3 1 1 0 c 180 rotate initclip}for showpage

---

<div class="post-metadata">

**Author:** ![Gavin\_Sinclair](https://avatars.discourse-cdn.com/v4/letter/g/db5fbb/32.png) [@Gavin\_Sinclair](https://rubytalk.org/u/Gavin_Sinclair)\
**Post date:** [9 October 2002 11:15 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/2 "2002-10-09T11:15:28Z")

</div>

> [all code snipped]  
> but unfortunately, the default value for the Hash is shared by all  
> elements, rather than copied, as I expected.
> 
> I was wondering if this was a conscious design decision, or an oversight.  
> I finally ended up with the much uglier
> 
> Is there another way to avoid the explicit tests?

It’s a common problem (are you listening, Bill :).

I don’t know about the design decision, but can offer a nicer solution. Others  
will undoubtably have others; I remember there being some clever ones expressed  
in the past, but not what they are ☹

Anyway:

(hash[key] ||= ) \<\< value

Make sense?

Gavin

> **···**
>
> From: “John Tromp” [tromp@serpens.ins.cwi.nl](mailto:tromp@serpens.ins.cwi.nl)

---

<div class="post-metadata">

**Author:** ![Bulat\_Ziganshin](https://avatars.discourse-cdn.com/v4/letter/b/ba8739/32.png) [@Bulat\_Ziganshin](https://rubytalk.org/u/Bulat_Ziganshin)\
**Post date:** [9 October 2002 11:32 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/3 "2002-10-09T11:32:09Z")

</div>

Hello Gavin,

Wednesday, October 09, 2002, 3:15:28 PM, you wrote:

> > but unfortunately, the default value for the Hash is shared by all  
> > elements, rather than copied, as I expected.  
> > It’s a common problem (are you listening, Bill :).

i think, next question will be about

a = .fill(‘x’,0,2)  
a[0] \<\< ‘y’  
p a

🙂

> **···**
>
> –  
> Best regards,  
> Bulat [mailto:bulatz@integ.ru](mailto:bulatz@integ.ru)

---

<div class="post-metadata">

**Author:** ![George\_Ogata1](https://avatars.discourse-cdn.com/v4/letter/g/d9b06d/32.png) [@George\_Ogata1](https://rubytalk.org/u/George_Ogata1)\
**Post date:** [9 October 2002 12:11 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/4 "2002-10-09T12:11:35Z")

</div>

Gavin Sinclair wrote:

> From: “John Tromp” [tromp@serpens.ins.cwi.nl](mailto:tromp@serpens.ins.cwi.nl)
> 
> > [all code snipped]  
> > but unfortunately, the default value for the Hash is shared by all  
> > elements, rather than copied, as I expected.
> > 
> > I was wondering if this was a conscious design decision, or an oversight.  
> > I finally ended up with the much uglier
> > 
> > Is there another way to avoid the explicit tests?
> 
> It’s a common problem (are you listening, Bill :).
> 
> I don’t know about the design decision, but can offer a nicer solution.  
> Others will undoubtably have others; I remember there being some clever  
> ones expressed in the past, but not what they are ☹
> 
> Anyway:
> 
> (hash[key] ||= ) \<\< value
> 
> Make sense?
> 
> Gavin

This seems similar to the problem with Array.new:

Array.new(5, ) will put the same “” in each cell, so:

a = Array.new(5, )  
a[2] \<\< 3  
a # [[3],[3],[3],[3],[3]]

If you’re using 1.7?+, you can give a block to the constructor which is  
evaluated to get the value of each cell:

a = Array.new(5) {}  
a[2] \<\< 3  
a # [,,[3],,]

A similar thing seems to exist for Hash:

h = Hash.new()  
h[:a] \<\<= :x  
h[:b] \<\<= :y  
h[:a] #[:x, :y]  
h = Hash.new {}  
h[:a] \<\<= :x  
h[:b] \<\<= :y  
h[:a] # [:x]

Strange thing is I can’t find this documented anywhere. Not even at  
[http://www.pragmaticprogrammer.com/ruby/new\_features.html](http://www.pragmaticprogrammer.com/ruby/new_features.html). Shouldn’t this  
be added there?

---

<div class="post-metadata">

**Author:** ![Billtj](https://avatars.discourse-cdn.com/v4/letter/b/85e7bf/32.png) [@Billtj](https://rubytalk.org/u/Billtj)\
**Post date:** [9 October 2002 18:52 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/5 "2002-10-09T18:52:19Z")

</div>

Hi Gavin,

Yes, I am listening. Actually the first time I saw the post, it came  
to my mind to include this as an addition to item #7 in the  
“newcomers” list. I guess I will include the new block feature in 1.7  
also (although, still [ruby-lang.org](http://ruby-lang.org) lists 1.6.7 as the stable version,  
and usually I don’t want to deal with some pre-release versions).

I am still “crippled”; our NNTP server seems still not working  
properly. Some messages are missing and the surviving messages seems  
to have hours of delay. So I am listening and posting from google,  
but then google also has hours of delay, although it seems that all  
the messages are complete. Definitely I cannot participate in an  
“almost-real-time” mode for a while…

Regards,

Bill

> **···**
>
> ===============================================================================  
> “Gavin Sinclair” [gsinclair@soyabean.com.au](mailto:gsinclair@soyabean.com.au) wrote in message news:[0e9f01c26f84$f4f0c060$526332d2@nosedog](mailto:0e9f01c26f84$f4f0c060$526332d2@nosedog)…
> 
> > From: “John Tromp” [tromp@serpens.ins.cwi.nl](mailto:tromp@serpens.ins.cwi.nl)
> > 
> > > [all code snipped]  
> > > but unfortunately, the default value for the Hash is shared by all  
> > > elements, rather than copied, as I expected.
> > > 
> > > I was wondering if this was a conscious design decision, or an oversight.  
> > > I finally ended up with the much uglier
> > > 
> > > Is there another way to avoid the explicit tests?
> > 
> > It’s a common problem (are you listening, Bill :).

---

<div class="post-metadata">

**Author:** ![Billtj](https://avatars.discourse-cdn.com/v4/letter/b/85e7bf/32.png) [@Billtj](https://rubytalk.org/u/Billtj)\
**Post date:** [9 October 2002 19:32 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/6 "2002-10-09T19:32:21Z")

</div>

Hi,

Actually it is even a problem for myself when I tried to put an  
example in the newcomers list 🙂 (Well… it is a list for newcomers  
from a newcomer 🙂 )

I am using ruby 1.6.7 (2002-03-01) [i686-linux] and I got the  
following:

hsh = Hash.new ()  
hsh[‘a’] \<\< ‘a’  
puts hsh.length # \>\> 0 ???

I think this example is equivalent as the code in the original post,  
but it seems it does not work for me. Any glitch in the code above?  
(I think I have some sense on what’s actually going on, but I want to  
imitate the code as in the original post.)

Regards,

Bill

> **···**
>
> =============================================================================  
> “Gavin Sinclair” [gsinclair@soyabean.com.au](mailto:gsinclair@soyabean.com.au) wrote in message news:[0e9f01c26f84$f4f0c060$526332d2@nosedog](mailto:0e9f01c26f84$f4f0c060$526332d2@nosedog)…
> 
> > It’s a common problem (are you listening, Bill :).

---

<div class="post-metadata">

**Author:** ![Gavin\_Sinclair](https://avatars.discourse-cdn.com/v4/letter/g/db5fbb/32.png) [@Gavin\_Sinclair](https://rubytalk.org/u/Gavin_Sinclair)\
**Post date:** [9 October 2002 12:16 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/7 "2002-10-09T12:16:58Z")

</div>

> This seems similar to the problem with Array.new:
> 
> Array.new(5, ) will put the same “” in each cell, so:
> 
> a = Array.new(5, )  
> a[2] \<\< 3  
> a # [[3],[3],[3],[3],[3]]
> 
> If you’re using 1.7?+, you can give a block to the constructor which is  
> evaluated to get the value of each cell:
> 
> a = Array.new(5) {}  
> a[2] \<\< 3  
> a # [,,[3],,]

Very nice. When initialising an array-of-arrays like that, I usually do (I’m  
not usually a 1.7 user):

arr = (1…N).map { }

> A similar thing seems to exist for Hash:  
> […]

Gavin

> **···**
>
> From: “George Ogata” [g\_ogata@optushome.com.au](mailto:g_ogata@optushome.com.au)

---

<div class="post-metadata">

**Author:** ![Christian\_Szegedy](https://avatars.discourse-cdn.com/v4/letter/c/ebca7d/32.png) [@Christian\_Szegedy](https://rubytalk.org/u/Christian_Szegedy)\
**Post date:** [9 October 2002 12:51 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/8 "2002-10-09T12:51:41Z")

</div>

George Ogata wrote:

> h[:a] \<\<= :x

Why “\<\<=”?

I know, I know,… this is only an example, but…

---

<div class="post-metadata">

**Author:** ![David\_Alan\_Black1](https://avatars.discourse-cdn.com/v4/letter/d/278dde/32.png) [@David\_Alan\_Black1](https://rubytalk.org/u/David_Alan_Black1)\
**Post date:** [9 October 2002 20:24 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/9 "2002-10-09T20:24:09Z")

</div>

Hi –

> **···**
>
> On Thu, 10 Oct 2002, Bill Tj wrote:
> 
> > I am using ruby 1.6.7 (2002-03-01) [i686-linux] and I got the  
> > following:
> > 
> > hsh = Hash.new ()  
> > hsh[‘a’] \<\< ‘a’  
> > puts hsh.length # \>\> 0 ???
> 
> What you’re doing is adding ‘a’ to that default array, rather  
> than setting an ‘a’ key. So the above is the same as if you  
> had done:
> 
> hsh = Hash.new([‘a’])
> 
> hsh still doesn’t have any keys, just a default value.
> 
> David
> 
> –  
> David Alan Black | Register for RubyConf 2002!  
> home: [dblack@candle.superlink.net](mailto:dblack@candle.superlink.net) | November 1-3  
> work: blackdav@shu.edu | Seattle, WA, USA  
> Web: [http://pirate.shu.edu/~blackdav](http://pirate.shu.edu/~blackdav) | [http://www.rubyconf.com](http://www.rubyconf.com)

---

<div class="post-metadata">

**Author:** ![Gavin\_Sinclair](https://avatars.discourse-cdn.com/v4/letter/g/db5fbb/32.png) [@Gavin\_Sinclair](https://rubytalk.org/u/Gavin_Sinclair)\
**Post date:** [10 October 2002 00:05 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/10 "2002-10-10T00:05:30Z")

</div>

> Hi Gavin,  
> […]  
> I am still “crippled”; our NNTP server seems still not working  
> properly. Some messages are missing and the surviving messages seems  
> to have hours of delay. So I am listening and posting from google,  
> but then google also has hours of delay, although it seems that all  
> the messages are complete. Definitely I cannot participate in an  
> “almost-real-time” mode for a while…
> 
> Regards,
> 
> Bill

Heh, try living in Australia. Receive ~80 messages in the morning and a tiny  
trickle throughout the day.

Gavin

> **···**
>
> From: “Bill Tj” [billtj@glue.umd.edu](mailto:billtj@glue.umd.edu)

---

<div class="post-metadata">

**Author:** ![MikkelFJ1](https://avatars.discourse-cdn.com/v4/letter/m/ce7236/32.png) [@MikkelFJ1](https://rubytalk.org/u/MikkelFJ1)\
**Post date:** [10 October 2002 00:32 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/11 "2002-10-10T00:32:42Z")

</div>

“Gavin Sinclair” [gsinclair@soyabean.com.au](mailto:gsinclair@soyabean.com.au) wrote in message  
news:0f1701c26ff0$8e979c50$526332d2@nosedog…

> Heh, try living in Australia. Receive ~80 messages in the morning and a  
> tiny  
> trickle throughout the day.

That must by me, staying up all at night in Denmark 🙂

Mikkel

---

<div class="post-metadata">

**Author:** ![Billtj](https://avatars.discourse-cdn.com/v4/letter/b/85e7bf/32.png) [@Billtj](https://rubytalk.org/u/Billtj)\
**Post date:** [10 October 2002 13:14 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/12 "2002-10-10T13:14:42Z")

</div>

Hi David,

Exactly. That’s why in the original post

nbs = Hash.new()  
while line = gets  
if line =~ /p(\d+) – p(\d+)/  
u, v = $1.to\_i, $2.to\_i  
nbs[u] \<\< v  
nbs[v] \<\< u  
end  
end

although it might seem that the “clue” was that the problem is “a  
single array which is shared by many hash elements”, probably the more  
immediate problem is actually “the use of hash default value as  
(intended) lvalue instead of rvalue”?

I am really curious whether the original poster got “overlapped  
arrays” problem , or “nbs.length == 0” problem, or both, when the code  
finished executing. This is because although

```
nbs[12]

```

may return a non-empty array,

```
nbs.each_key {|key| puts key}

```

does not print anything, as nbs.length == 0. Is this a commonly known  
Ruby hash feature?

To summarize, it seems that

```
"Given a right key, nbs may return something, but we cannot know
what those keys are from nbs alone." :)

```

Maybe we can use something like this for cryptography such as hiding  
data in our Ruby code so that the “naughty” user cannot do too much  
damage? 🙂

Regards,

Bill

> **···**
>
> =============================================================================  
> [dblack@candle.superlink.net](mailto:dblack@candle.superlink.net) wrote in message news:[Pine.LNX.4.44.0210091624440.16424-100000@candle.superlink.net](mailto:Pine.LNX.4.44.0210091624440.16424-100000@candle.superlink.net)…
> 
> > Hi –
> 
> > What you’re doing is adding ‘a’ to that default array, rather  
> > than setting an ‘a’ key. So the above is the same as if you  
> > had done:
> > 
> > hsh = Hash.new([‘a’])
> > 
> > hsh still doesn’t have any keys, just a default value.

---

<div class="post-metadata">

**Author:** ![George\_Ogata1](https://avatars.discourse-cdn.com/v4/letter/g/d9b06d/32.png) [@George\_Ogata1](https://rubytalk.org/u/George_Ogata1)\
**Post date:** [10 October 2002 14:14 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/13 "2002-10-10T14:14:47Z")

</div>

Christian Szegedy wrote:

> Why “\<\<=”?

Guess I wasn’t thinking… thanks.

---

<div class="post-metadata">

**Author:** ![David\_Alan\_Black1](https://avatars.discourse-cdn.com/v4/letter/d/278dde/32.png) [@David\_Alan\_Black1](https://rubytalk.org/u/David_Alan_Black1)\
**Post date:** [10 October 2002 13:56 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/14 "2002-10-10T13:56:00Z")

</div>

Hi –

> **···**
>
> On Thu, 10 Oct 2002, Bill Tj wrote:
> 
> > I am really curious whether the original poster got “overlapped  
> > arrays” problem , or “nbs.length == 0” problem, or both, when the code  
> > finished executing. This is because although
> > 
> > ```
> > nbs[12]
> > 
> > ```
> > 
> > may return a non-empty array,
> > 
> > ```
> > nbs.each_key {|key| puts key}
> > 
> > ```
> > 
> > does not print anything, as nbs.length == 0. Is this a commonly known  
> > Ruby hash feature?
> 
> Yes: the presence of a default value (even a non-nil one) is not the  
> same as the presence of a key/value pair. So nbs is an empty hash  
> whose default value happens not to be nil.
> 
> (Consider the alternative. If setting a default actually populated  
> the hash, what would hsh.keys consist of? 🙂
> 
> David
> 
> –  
> David Alan Black | Register for RubyConf 2002!  
> home: [dblack@candle.superlink.net](mailto:dblack@candle.superlink.net) | November 1-3  
> work: blackdav@shu.edu | Seattle, WA, USA  
> Web: [http://pirate.shu.edu/~blackdav](http://pirate.shu.edu/~blackdav) | [http://www.rubyconf.com](http://www.rubyconf.com)

---

<div class="post-metadata">

**Author:** ![William\_Djaja\_Tjokr1](https://avatars.discourse-cdn.com/v4/letter/w/87869e/32.png) [@William\_Djaja\_Tjokr1](https://rubytalk.org/u/William_Djaja_Tjokr1)\
**Post date:** [10 October 2002 14:34 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/15 "2002-10-10T14:34:53Z")

</div>

Hi David,

Probably the problem is not that bad. A hash value can be populated with  
the default value only when the hash[key] is called as an rvalue. As an  
(imaginary) example:

```
hsh = Hash.new ('a')
puts hsh['x'] # >> 'a'
puts hsh['y'] # >> 'a'
hsh.keys # -> ['x', 'y']

```

Regards,

Bill

> **···**
>
> =========================================================================  
> [dblack@candle.superlink.net](mailto:dblack@candle.superlink.net) wrote:
> 
> > (Consider the alternative. If setting a default actually populated  
> > the hash, what would hsh.keys consist of? 🙂

---

<div class="post-metadata">

**Author:** ![David\_Alan\_Black1](https://avatars.discourse-cdn.com/v4/letter/d/278dde/32.png) [@David\_Alan\_Black1](https://rubytalk.org/u/David_Alan_Black1)\
**Post date:** [10 October 2002 15:02 UTC](https://rubytalk.org/t/strange-hash-default-behaviour/2211/16 "2002-10-10T15:02:53Z")

</div>

Hi –

> Hi David,
> 
> Probably the problem is not that bad. A hash value can be populated with  
> the default value only when the hash[key] is called as an rvalue. As an  
> (imaginary) example:
> 
> ```
> hsh = Hash.new ('a')
> puts hsh['x'] # >> 'a'
> puts hsh['y'] # >> 'a'
> hsh.keys # -> ['x', 'y']
> 
> ```

You can do that like this:

class Hash  
alias :oldget :  
def   
v = oldget(k)  
self[k] = v unless has\_key?(k)  
v  
end  
end

It’s a bit too “magic” for my taste. It fights against the notion of  
a default, by only defaulting once. So if you change the default  
dynamically, hsh[“x”] won’t default to the new default:

puts hsh[“k”] # “a”

# …

hsh.default = “b”  
puts hsh[“k”] # “a”

David

> **···**
>
> On Thu, 10 Oct 2002, William Djaja Tjokroaminata wrote:
> 
> –  
> David Alan Black | Register for RubyConf 2002!  
> home: [dblack@candle.superlink.net](mailto:dblack@candle.superlink.net) | November 1-3  
> work: blackdav@shu.edu | Seattle, WA, USA  
> Web: [http://pirate.shu.edu/~blackdav](http://pirate.shu.edu/~blackdav) | [http://www.rubyconf.com](http://www.rubyconf.com)
