tag:www.rhnh.net,2008:/hash Hash - Xavier Shay's Blog 2008-05-10T11:37:55Z Enki Xavier Shay notreal@rhnh.net tag:www.rhnh.net,2008:Post/779 2008-05-10T11:37:00Z 2008-05-10T11:37:55Z Hash trumps case <table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt>7<tt> </tt>8<tt> </tt>9<tt> </tt><strong>10</strong><tt> </tt>11<tt> </tt>12<tt> </tt>13<tt> </tt>14<tt> </tt>15<tt> </tt>16<tt> </tt>17<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># Two equivalent functions</span><tt> </tt><span class="r">def</span> <span class="fu">rgb</span>(color)<tt> </tt> <span class="r">case</span> color<tt> </tt> <span class="r">when</span> <span class="sy">:red</span> <span class="r">then</span> <span class="s"><span class="dl">'</span><span class="k">ff0000</span><span class="dl">'</span></span><tt> </tt> <span class="r">when</span> <span class="sy">:green</span> <span class="r">then</span> <span class="s"><span class="dl">'</span><span class="k">00ff00</span><span class="dl">'</span></span><tt> </tt> <span class="r">when</span> <span class="sy">:blue</span> <span class="r">then</span> <span class="s"><span class="dl">'</span><span class="k">0000ff</span><span class="dl">'</span></span><tt> </tt> <span class="r">else</span> <span class="s"><span class="dl">'</span><span class="k">000000</span><span class="dl">'</span></span> <span class="c"># Default to black</span><tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="r">def</span> <span class="fu">rgb2</span>(color)<tt> </tt> {<tt> </tt> <span class="sy">:red</span> =&gt; <span class="s"><span class="dl">'</span><span class="k">ff0000</span><span class="dl">'</span></span>,<tt> </tt> <span class="sy">:green</span> =&gt; <span class="s"><span class="dl">'</span><span class="k">00ff00</span><span class="dl">'</span></span>,<tt> </tt> <span class="sy">:blue</span> =&gt; <span class="s"><span class="dl">'</span><span class="k">0000ff</span><span class="dl">'</span></span><tt> </tt> }[color] || <span class="s"><span class="dl">'</span><span class="k">000000</span><span class="dl">'</span></span><tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>Even though these functions are equivalent, the second carries more semantic weight &#8211; it maps a symbol directly to a color. The <code>case</code> sample makes no such guarantees since you can execute any arbitrary code in the <code>then</code> block. In addition, a hash is easier to work with &#8211; you can easily iterate over the keys, extract to another method if you need reuse, or query it for other properties (for example, 3 colors are available). It is also easier to read &#8211; both aesthetically and because it contains fewer tokens. In almost all circumstances I will prefer a hash over a case statement.</p> <p><strong>Relationships in data are easier to comprehend and manipulate than relationships in code.</strong></p> tag:www.rhnh.net,2008:Post/234 2007-11-15T05:55:00Z 2007-11-15T06:02:33Z Hash#translate_keys_and_values <table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt>7<tt> </tt>8<tt> </tt>9<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">module</span> <span class="cl">CoreExtensions</span><tt> </tt> <span class="r">module</span> <span class="cl">Hash</span> <tt> </tt> <span class="r">def</span> <span class="fu">translate_keys_and_values</span>(&amp;block)<tt> </tt> inject({}) {|a, (key, value)| a.update(block.call(key) =&gt; block.call(value))}<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="co">Hash</span>.send(<span class="sy">:include</span>, <span class="co">CoreExtensions</span>::<span class="co">Hash</span>)<tt> </tt></pre></td> </tr></table> <p>It&#8217;s like <code>symbolize_keys</code> but a bit more flexible. It calls the block for every key and value in the hash. Of course you could tune it just do keys or values if you wanted. I do not want!</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">{<span class="s"><span class="dl">&quot;</span><span class="k">1</span><span class="dl">&quot;</span></span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">2</span><span class="dl">&quot;</span></span>}.translate_keys_and_values(&amp;<span class="sy">:to_i</span>) <span class="c"># =&gt; {1 =&gt; 2}</span><tt> </tt>{<span class="i">1</span> =&gt; <span class="i">2</span>}.translate_keys_and_values {|x| x + <span class="i">1</span> } <span class="c"># =&gt; {2 =&gt; 3}</span><tt> </tt></pre></td> </tr></table> tag:www.rhnh.net,2008:Post/216 2007-11-05T23:21:00Z 2007-11-05T23:36:36Z Facets patch <table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">$ svn log svn://rubyforge.org/var/svn/facets/trunk -r 383 -v<tt> </tt>---------------------------------------------------------------------</pre></td> </tr></table> <p>r383 | transami | 2007-11-03 23:31:54 +1100 (Sat, 03 Nov 2007) | 2 lines Changed paths: M /trunk/lib/core/facets/hash/op.rb M /trunk/test/unit/hash/test_op.rb</p> <p>Fixed bug in Hash#- Thanks to Xavier Shay.</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">--- ruby<tt> </tt>require 'facets/hash/op'<tt> </tt>{:a =&gt; 1, :b =&gt; 2, :c =&gt; 3} - [:a, :b] # =&gt; {:c =&gt; 3}<tt> </tt>{:a =&gt; 1, :b =&gt; 2, :c =&gt; 3} - {:a =&gt; 1, :b =&gt; 99} # =&gt; {:b =&gt; 2, :c =&gt; 3}<tt> </tt></pre></td> </tr></table> <p>It may be small, but it&#8217;s authentic. In the 2.0.5 gem.</p> tag:www.rhnh.net,2008:Post/180 2007-10-23T07:46:00Z 2007-10-23T07:48:34Z Enumerable#inject is my favourite method <p><em>Combines the elements of enum by applying the block to an accumulator value (memo) and each element in turn. At each step, memo is set to the value returned by the block. &#8211; <a href="http://www.ruby-doc.org/core/classes/Enumerable.html#M003171">RubyDoc</a></em></p> <p>It just doesn&#8217;t sound very helpful. I must confess, it isn&#8217;t something I use everyday. But I love that when you do want to use it, it is oh so sweet. The canonical example is summing the elements in an array:</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">[<span class="i">1</span>,<span class="i">2</span>,<span class="i">3</span>].inject(<span class="i">0</span>) {|sum, n| sum + n} <span class="c"># =&gt; 6</span><tt> </tt></pre></td> </tr></table> <p>Probably the most used pattern is converting an array to a hash:</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">[<span class="i">1</span>,<span class="i">2</span>,<span class="i">3</span>].inject({}) {|a, v| a.update(v =&gt; v * <span class="i">2</span>)} <span class="c"># =&gt; {1 =&gt; 2, 2 =&gt; 4, 3 =&gt; 6}</span><tt> </tt></pre></td> </tr></table> <p>Someone in <span class="caps">IRC</span> today wanted a nested send, something like @&#8221;string&#8221;.send(&#8220;trim.downcase&#8221;)</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="s"><span class="dl">&quot;</span><span class="k">trim.downcase</span><span class="dl">&quot;</span></span>.split(<span class="s"><span class="dl">'</span><span class="k">.</span><span class="dl">'</span></span>).inject(<span class="s"><span class="dl">&quot;</span><span class="k">HELLO </span><span class="dl">&quot;</span></span>) {|obj, method| obj.send(method)} <span class="c"># =&gt; &quot;hello&quot;</span><tt> </tt></pre></td> </tr></table> <p>What do you inject?</p> tag:www.rhnh.net,2008:Post/125 2007-07-26T07:17:00Z 2007-08-15T07:26:16Z This is stupid: Hash#select vs reject <p>A little consistency would be nice&#8230;</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">{<span class="i">1</span>=&gt;<span class="i">1</span>, <span class="i">2</span>=&gt;<span class="i">2</span>, <span class="i">3</span>=&gt;<span class="i">3</span>}.reject {|key, value| key != <span class="i">1</span> } <span class="c"># =&gt; {1=&gt;1}</span><tt> </tt>{<span class="i">1</span>=&gt;<span class="i">1</span>, <span class="i">2</span>=&gt;<span class="i">2</span>, <span class="i">3</span>=&gt;<span class="i">3</span>}.select {|key, value| key == <span class="i">1</span> } <span class="c"># =&gt; [[1, 1]]</span><tt> </tt></pre></td> </tr></table>