Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting hash equal to another hash in Ruby

Tags:

hashtable

ruby

I want to keep track of the counts of some arbitarily-named strings and then reset the counts to zero. My thought was to do the following:

reset_hash={"string1"=>0,"string2"=>0,"string3"=>0}
=> {"string1"=>0, "string2"=>0, "string3"=>0} 

new_hash = reset_hash
=> {"string1"=>0, "string2"=>0, "string3"=>0} 

new_hash["string1"]=1
new_hash["string3"]=1
new_hash
=> {"string1"=>1, "string2"=>0, "string3"=>1}

...

Now I want to reset new_hash back to reset_hash:

new_hash = reset_hash
=> {"string1"=>1, "string2"=>0, "string3"=>1}
reset_hash
=> {"string1"=>1, "string2"=>0, "string3"=>1} 

What is going on here? It seems that reset_hash has actually been set to new_hash, which is the opposite of what I wanted. How do I implement the desired behavior?

like image 762
Evan Zamir Avatar asked Jul 16 '12 20:07

Evan Zamir


People also ask

How do you combine hashes in Ruby?

We can merge two hashes using the merge() method. When using the merge() method: Each new entry is added to the end. Each duplicate-key entry's value overwrites the previous value.

Can you compare two hashes?

Equality—Two hashes are equal if they each contain the same number of keys and if each key-value pair is equal to (according to Object#== ) the corresponding elements in the other hash. The orders of each hashes are not compared.

How do you add hash to hash?

Merge two hashes On of the ways is merging the two hashes. In the new hash we will have all the key-value pairs of both of the original hashes. If the same key appears in both hashes, then the latter will overwrite the former, meaning that the value of the former will disappear. (See the key "Foo" in our example.)

How do you add a value to an existing hash in Ruby?

You might get your key and value from user input, so you can use Ruby . to_sym can convert a string to a symbol, and . to_i will convert a string to an integer.


2 Answers

When you have a variable pointing to an object, you really just have a reference to the object. If both a and b point to the hash {1=>3, "foo" => 54}, changing a or b will change the other.

However, you can use a combination of two methods to make what you want.

A default value for the hash:

new_hash = Hash.new(0)

This gives unused values a default value of 0:

new_hash["eggs"]  # -> 0

You can then add counts:

new_hash["string1"] += 1 # => 1

When you're done just call

new_hash.clear # => {}

and your hash will be reset, but new accesses will still default to 0.

Note that if you set the default value to a type of object other than a number you may be able to change things due to the whole reference issue noted above.

irb(main):031:0> b = Hash.new("Foo") #=> {}
irb(main):032:0> b[3] #=> "Foo"
irb(main):033:0> b[33] #=> "Foo"
irb(main):034:0> b[33].upcase! #=> "FOO"
irb(main):035:0> b[3] # => "FOO"

To get around this you can pass a block to you hash:

h = Hash.new {|hash, key| hash[key] = "new default value"}

This creates a new object at key each time, so changes to one won't ripple:

d = Hash.new{ |hash,key| hash[key] = "string"} #=> {}
d[3] # => "string"
d[3].upcase! #=> "STRING"
d[5] #=> "string"
like image 126
Paul Rubel Avatar answered Oct 03 '22 20:10

Paul Rubel


As others mentioned you have to use clone. Your task should look like this:

reset_hash={"string1"=>0,"string2"=>0,"string3"=>0}
new_hash = reset_hash.clone

new_hash["string1"]=1
new_hash["string3"]=1
new_hash

new_hash = reset_hash.clone
reset_hash
like image 35
Иван Бишевац Avatar answered Oct 03 '22 20:10

Иван Бишевац