11

My code is:

hash = { two: 2, three: 3 }

def hash_add(hash, new_key, new_value)
  temp_hash = {}

  temp_hash[new_key.to_sym] = new_value
  temp_hash.merge!(hash)
  hash = temp_hash
  puts hash

end

hash_add(hash, 'one', 1)

Within the method, puts hash returns { :one => 1, :two => 2, :three => 3 }, but when hash1 is put to the method, it remains unchanged afterward. It's like the assignment isn't carrying itself outside of the function.

I guess I could return the updated hash and set the hash I want to change to it outside the method:

hash = hash_add(hash, 'one', 1)

But I just don't see why the assignment I give to the hash does not stick outside of the method.

I have this, which works:

def hash_add(hash, new_key, new_value)
  temp_hash = {}

  temp_hash[new_key.to_sym] = new_value
  temp_hash.merge!(hash)
  hash.clear

  temp_hash.each do |key, value|
    hash[key] = value
  end
end

Which gives me what I'm wanting when this method is called, but it just seems a little excessive to have to rebuild the hash like that.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Kyle Shike
  • 345
  • 1
  • 4
  • 12
  • you have to understand that in most dynamic languages, variables are nothing but references. the first solution creates a local reference (rebinds a variable) to the hash, it doesn't update the original reference. – Karoly Horvath Sep 18 '13 at 21:20

5 Answers5

18

How about this?

hash1 = { two: 2, three: 3 }

#add a new key,value 
hash1 = Hash[:one,1].merge!(hash1) #=> {:one=>1, :two=>2, :three=>3}

Example #2:

h = { two: 2, three: 3 }

def hash_add(h,k,v)
  Hash[k.to_sym,v].merge!(h)
end

h = hash_add(h, 'one', 1) #=> {:one=>1, :two=>2, :three=>3}
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Bala
  • 11,068
  • 19
  • 67
  • 120
7

Ruby passes objects to methods by value, but the value is the reference to the object, so when you set hash=temp_hash within the add_hash method, that change only applies inside the method. The value of hash outside the method is unchanged.

def hash_add(hash, new_key, new_value)
  temp_hash = {}

  temp_hash[new_key.to_sym] = new_value
  temp_hash.merge!(hash)
  hash = temp_hash
  hash
end
h2 = hash_add(hash, 'one', 1)
hash
=> {:two=>2, :three=>3}
h2
=>{:one=>1, :two=>2, :three=>3}

If you want hash to be updated, you need to replace the contents of hash rather than re-point hash at a new object as you did with the clear and re-adding the values. You can also do it with the replace method.

def hash_add(hash, new_key, new_value)
  temp_hash = {}

  temp_hash[new_key.to_sym] = new_value
  temp_hash.merge!(hash)
  hash.replace temp_hash
end

There are some good diagrams about pass by value in "Is Ruby pass by reference or by value?"

Community
  • 1
  • 1
GregA100k
  • 1,385
  • 1
  • 11
  • 16
  • if it is always passed by value then how come the array is updated in this case. `a = [1,2,3] def meth(a,v) a << v end; meth(a,34) #=> a is now [1, 2, 3, 34]` – Bala Sep 19 '13 at 05:51
  • Because you are mutating the array. When you change the array, the array changes (duh). That's just how shared mutable state works. – Jörg W Mittag Sep 20 '13 at 01:27
2

NOTE: this answer is old from times when Ruby 1.8 was still around.

In general, the class Hash in Ruby does not provide ordering. Behavior might differ between Ruby versions / implementations.

See also: Hash ordering preserved between iterations if not modified?

If you want ordering, you need to use the class OrderedHash which is provided through ActiveSupport

See: http://apidock.com/rails/ActiveSupport/OrderedHash

Tilo
  • 33,354
  • 5
  • 79
  • 106
0

At the end of the function you are just putsing the hash, not returning it. Perhaps if you changed puts hash to return hash it would work (I haven't tried it myself).

bigpotato
  • 26,262
  • 56
  • 178
  • 334
0

temp_hash is a local variable, which gets deleted once the function returns.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Bharat Jain
  • 654
  • 4
  • 6
  • But if the data that was attached to it is reassigned to a variable that isn't local, like hash1, shouldn't it stick around? – Kyle Shike Sep 18 '13 at 21:35