https://redmine.ruby-lang.org/https://redmine.ruby-lang.org/favicon.ico?17113305112015-01-08T00:02:10ZRuby Issue Tracking SystemRuby master - Bug #10713: Assigning default value for a Hash as an empty Array creating unpredictable resultshttps://redmine.ruby-lang.org/issues/10713?journal_id=508452015-01-08T00:02:10Zhsbt (Hiroshi SHIBATA)hsbt@ruby-lang.org
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Rejected</i></li></ul><p>It's expected behavior</p> Ruby master - Bug #10713: Assigning default value for a Hash as an empty Array creating unpredictable resultshttps://redmine.ruby-lang.org/issues/10713?journal_id=508462015-01-08T01:16:01Zduerst (Martin Dürst)duerst@it.aoyama.ac.jp
<ul></ul><p>Hiroshi SHIBATA wrote:</p>
<blockquote>
<p>It's expected behavior</p>
</blockquote>
<p>Hiroshi, can you tell us why it's expected behavior? It looks quite surprising.</p> Ruby master - Bug #10713: Assigning default value for a Hash as an empty Array creating unpredictable resultshttps://redmine.ruby-lang.org/issues/10713?journal_id=508472015-01-08T01:33:45Zdonburks (Don Burks)don@donburks.com
<ul></ul><p>Martin Dürst wrote:</p>
<blockquote>
<p>Hiroshi SHIBATA wrote:</p>
<blockquote>
<p>It's expected behavior</p>
</blockquote>
<p>Hiroshi, can you tell us why it's expected behavior? It looks quite surprising.</p>
</blockquote>
<p>I would agree that this is surprising behaviour. It would appear that in this case, the append operator is not re-assigning the value, the way it does any other time it is used. And it would appear to be not doing this specifically in the case where the default value for the Hash is specified as an empty array. I would like to understand why this behaviour is the way it is.</p> Ruby master - Bug #10713: Assigning default value for a Hash as an empty Array creating unpredictable resultshttps://redmine.ruby-lang.org/issues/10713?journal_id=508482015-01-08T01:38:21Zaustin (Austin Ziegler)halostatue@gmail.com
<ul></ul><p>It's quite expected because the default array is created exactly once:</p>
<p>letters = Hash.new([])<br>
letters.default.object_id # => 70310393550400<br>
letters[:a].object_id # => 70310393550400<br>
letters[:b].object_id # => 70310393550400</p>
<p>The pattern that Arvinder <em>wants</em> is:</p>
<p>letters = Hash.new { |h, k| h[k] = [] }<br>
letters.default # => nil<br>
letters.default_proc # => #<a href="Proc:0x007fe4d4099988@(irb):7" class="external">Proc:0x007fe4d4099988@(irb):7</a><br>
letters[:a].object_id # => 70310393174180<br>
letters[:b].object_id # => 70310393171680</p>
<p>It's possible to make the correct pattern do the "wrong thing":</p>
<p>array = []<br>
array.object_id # => 70310393550400<br>
letters = Hash.new { |h, k| h[k] = array }<br>
letters.default # => nil<br>
letters.default_proc # => #<a href="Proc:0x007fe4d4099988@(irb):7" class="external">Proc:0x007fe4d4099988@(irb):7</a><br>
letters[:a].object_id # => 70310393550400<br>
letters[:b].object_id # => 70310393550400</p>
<p>-a</p>
<p>On Wed, Jan 7, 2015 at 8:16 PM, <a href="mailto:duerst@it.aoyama.ac.jp" class="email">duerst@it.aoyama.ac.jp</a> wrote:</p>
<blockquote>
<p>Issue <a class="issue tracker-1 status-6 priority-4 priority-default closed" title="Bug: Assigning default value for a Hash as an empty Array creating unpredictable results (Rejected)" href="https://redmine.ruby-lang.org/issues/10713">#10713</a> has been updated by Martin Dürst.</p>
<p>Hiroshi SHIBATA wrote:</p>
<blockquote>
<p>It's expected behavior</p>
</blockquote>
<p>Hiroshi, can you tell us why it's expected behavior? It looks quite<br>
surprising.</p>
<hr>
<p>Bug <a class="issue tracker-1 status-6 priority-4 priority-default closed" title="Bug: Assigning default value for a Hash as an empty Array creating unpredictable results (Rejected)" href="https://redmine.ruby-lang.org/issues/10713">#10713</a>: Assigning default value for a Hash as an empty Array creating<br>
unpredictable results<br>
<a href="https://bugs.ruby-lang.org/issues/10713#change-50846" class="external">https://bugs.ruby-lang.org/issues/10713#change-50846</a></p>
<ul>
<li>Author: Arvinder Singh</li>
<li>Status: Rejected</li>
<li>Priority: Normal</li>
<li>Assignee:</li>
<li>Category: core</li>
<li>Target version: current: 2.2.0</li>
<li>ruby -v: ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]</li>
<li>Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN</li>
</ul>
<hr>
<p>Creating a Hash with a default value works fine, unless the default value<br>
is an empty Array.</p>
<p>E.g. the following returns an empty Hash...</p>
<pre><code>irb(main):001:0> letters = Hash.new([])
=> {}
irb(main):002:0> letters[:a] << 1
=> [1]
irb(main):003:0> letters[:a] << 2
=> [1, 2]
irb(main):004:0> letters[:a]
=> [1, 2]
irb(main):005:0> letters
=> {}
</code></pre>
<p>whereas the following code explicitly defining hash keys works.</p>
<pre><code>irb(main):001:0> letters = {a: [], b: []}
=> {:a=>[], :b=>[]}
irb(main):002:0> letters[:a] << 1
=> [1]
irb(main):003:0> letters[:a] << 2
=> [1, 2]
irb(main):004:0> letters[:a]
=> [1, 2]
irb(main):005:0> letters
=> {:a=>[1, 2], :b=>[]}
</code></pre>
<p>Is this an unpredictable(bug) or an expected behavior(feature). I tend to<br>
lean towards the former, but I might be wrong.</p>
<p>--<br>
<a href="https://bugs.ruby-lang.org/" class="external">https://bugs.ruby-lang.org/</a></p>
</blockquote>
<p>--<br>
Austin Ziegler * <a href="mailto:halostatue@gmail.com" class="email">halostatue@gmail.com</a> * <a href="mailto:austin@halostatue.ca" class="email">austin@halostatue.ca</a><br>
<a href="http://www.halostatue.ca/" class="external">http://www.halostatue.ca/</a> * <a href="http://twitter.com/halostatue" class="external">http://twitter.com/halostatue</a></p> Ruby master - Bug #10713: Assigning default value for a Hash as an empty Array creating unpredictable resultshttps://redmine.ruby-lang.org/issues/10713?journal_id=508532015-01-08T04:42:04Zdavid_macmahon (David MacMahon)davidm@astro.berkeley.edu
<ul></ul><p>Arvinder Singh wrote:</p>
<blockquote>
<pre><code>irb(main):001:0> letters = Hash.new([])
=> {}
irb(main):002:0> letters[:a] << 1
=> [1]
</code></pre>
</blockquote>
<p>The <code>letters[:a]</code> part of the second line returns the "default value" of the Hash because the <code>:a</code> key does not exist in the Hash. The <code><< 1</code> part pushes a <code>1</code> onto the end of the default value, but it does not assign the default value (and certainly not a modified copy of the default value) to the <code>:a</code> key of the Hash.</p>
<p>To get the behavior you expect/desire you can use <code>letters[:a] <<= 1</code>, which is equivalent to <code>letters[:a] = letters[:a] << 1</code>, but be careful when using Hashes with default objects:</p>
<pre><code>irb(main):001:0> letters = Hash.new([]) # Default object == single instance
=> {}
irb(main):002:0> letters[:a] <<= 1
=> [1]
irb(main):003:0> letters[:a]
=> [1]
irb(main):004:0> letters
=> {:a=>[1]}
irb(main):005:0> letters[:b] # Returns default object (same object as letters[:a])!
=> [1]
</code></pre>
<p>To get a different object for each fetch of a non-existent key, use a Hash with a default Proc that returns new object every time:</p>
<pre><code>irb(main):001:0> letters = Hash.new {[]} # Default Proc returns new instance each call
=> {}
irb(main):002:0> letters[:a] <<= 1
=> [1]
irb(main):003:0> letters[:a]
=> [1]
irb(main):004:0> letters
=> {:a=>[1]}
irb(main):005:0> letters[:b] # Returns different empty Array until letters[:b] is assigned
=> []
irb(main):006:0> letters[:b].object_id
=> 2152992900
irb(main):007:0> letters[:b].object_id
=> 2153118760
irb(main):008:0> letters[:b].object_id
=> 2153112180
irb(main):009:0> letters[:b] = letters[:b]
=> []
irb(main):010:0> letters[:b].object_id
=> 2156907140
irb(main):011:0> letters[:b].object_id
=> 2156907140
</code></pre>
<p>Given all the trickiness and because sometimes you don't have control over the creation of the Hash, I generally end up doing this instead:</p>
<pre><code>>> # h is any Hash, e.g. from method call
>> h[:a] ||= []
=> []
>> h[:a] << 1
=> [1]
# etc...
</code></pre>
<p>This uses the "||=" idiom, which can obliterate an existing <code>nil</code> or <code>false</code> value, so you still have to be a little careful (though it's very rarely an issue).</p>