From: Rob Biedenharn Date: 2008-09-20T03:41:01+09:00 Subject: Re: Hash default value question On Sep 19, 2008, at 1:40 PM, Glenn wrote: > Hi, > > Can anyone explain why hash_of_indexes1 in the code below works, but > hash_of_index2 doesn't? (I got the first method as a response to a > previous post.) > > What I'd like to get is a hash with a key value for each unique > value in the receiver array, and I'd like the value for each key to > be an array of integers corresponding to the index values of the > elements in the original array. The first method works, but the > second one doesn't. I figured if I created the hash in the second > method with a default value of an empty array, h[e.to_f] in the each > method of the hash would return an empty array the first time it > sees each key. But what actually happens is that the values for all > of the keys are the same when the hash's each method finishes -- > each value is an array with all of the index values in the receiver > array. > > class Array > def hash_of_indexes1 > h = Hash.new > each_with_index { |e, i| h[e.to_f] = Array( h[e.to_f] ) << i } > h > end > > def hash_of_indexes2 > h = Hash.new([]) > each_with_index { |e, i| h[e.to_f] = h[e.to_f] << i } > h > end > end > > [1, 2, 2, 3].hash_of_indexes1.inspect ## --> returns {1.0=>[0], > 3.0=>[3], 2.0=>[1, 2]} > > [1, 2, 2, 3].hash_of_indexes2.inspect ## --> returns {1.0=>[0, 1, 2, > 3], 3.0=>[0, 1, 2, 3], 2.0=>[0, 1, 2, 3]} > > Thanks for your help. The Hash.new([]) causes a single array to be used (shared) for the default value. Using the block form, Hash.new {[]}, would produce the behavior you want. It's common to see this used as: Hash.new {|h,k| h[k] = [] } if you want the key to exist in the hash when referenced (as you seem to expect here). class Array def hash_of_indexes3 h = Hash.new {|h,k| h[k] = []} each_with_index {|e,i| h[e] << i } h end end irb> [1, 2, 2, 3].hash_of_indexes3 => {1=>[0], 2=>[1, 2], 3=>[3]} I'm not sure why you wanted to call .to_f on each element, I left that out. -Rob > ----- Original Message ---- > From: William James > To: ruby-talk ML > Sent: Sunday, September 14, 2008 4:02:54 PM > Subject: Re: Passing a method call into a method > > On Sep 14, 8:18 am, Glenn wrote: >> [Note: parts of this message were removed to make it a legal post.] >> >> Hi, >> >> I am trying to figure out how to pass a method call into a method. >> >> I wrote a generic method that call be called on an array of >> numbers, and returns an array of the index values in the receiver >> that meet a specified condition: >> >> class Array >> def hash_of_indexes >> if self.empty? >> 'The array is empty.' >> else >> h = {} >> self.each_with_index { |e, i| h.include?(e.to_f) ? h[e.to_f] >> << i : h[e.to_f] = [i] } >> h >> end >> end > > > class Array > def hash_of_indexes > h = {} > each_with_index{ |e,i| > e = e.to_f > h[e] = Array( h[e] ) << i } > h > end > end Rob Biedenharn http://agileconsultingllc.com Rob@AgileConsultingLLC.com +1 513-295-4739 Skype: rob.biedenharn