Hash default value question

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.

···

----- Original Message ----
From: William James <w_a_x_man@yahoo.com>
To: ruby-talk ML <ruby-talk@ruby-lang.org>
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 <glenn_r...@yahoo.com> 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

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.)

snip

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]}

With hash_of_indexes2, the array assigned to the each hash key is the
same Array object. You can tell this by comparing their __id__ tags
(apologies for lazy style)...

a = 1,2,3,4
b = 1,2,3,4

a == b
=> true
a.__id__ == b.__id__
=> true

a = Array([1,2,3,4])
b = Array([1,2,3,4])

a == b
=> true
a.__id__ == b.__id__
=> false

Todd

···

On Fri, Sep 19, 2008 at 12:40 PM, Glenn <glenn_ritz@yahoo.com> 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

[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

···

On Sep 19, 2008, at 1:40 PM, Glenn wrote:

----- Original Message ----
From: William James <w_a_x_man@yahoo.com>
To: ruby-talk ML <ruby-talk@ruby-lang.org>
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 <glenn_r...@yahoo.com> 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