Assume we have
hash = { a: 1, b: 2 }
hash2 = hash.to_hash
when I change value in hash2 it affect value in hash
hash2[:a] = 5
# hash[:a] = 5
Why ruby acts like that ?
How to fix this ?
Calling to_hash on a Hash returns itself. hash and hash2 are the same object.
2.6.5 :001 > hash = { a: 1, b: 2 }
=> {:a=>1, :b=>2}
2.6.5 :002 > hash2 = hash.to_hash
=> {:a=>1, :b=>2}
2.6.5 :003 > hash.object_id
=> 70244375263800
2.6.5 :004 > hash2.object_id
=> 70244375263800
Related
I'm using Ruby 2.4. I'm confused about the whole reference vs value thing when copying the contents of a hash. How do I copy the contents of one hash into another without changing the reference (memory address?) of the hash? Below is the example of the problem I'm having ....
2.4.0 :003 > def copy_hash(h)
2.4.0 :004?> new_hash = {"a" => 1}
2.4.0 :005?> h = new_hash
2.4.0 :006?> end
=> :copy_hash
2.4.0 :007 > h = {"b" => 2}
=> {"b"=>2}
2.4.0 :008 > copy_hash(h)
=> {"a"=>1}
2.4.0 :009 > h
=> {"b"=>2}
In the function, I'm assigning the parameter to a new hash ...
h = new_hash
But once the function returns the original hash is unchanged. What's the right way to change the hash in the function so that when it returns the value of the parameter is also changed? That is, if my hash started out as
{"b" => 2}
I'd like the value to be
{"a"=>1}
after I invoke the "copy_hash" function.
You can use Hash#replace to replace the contents of the hash:
def copy(h)
new_hash = { 'a' => 1 }
h.replace(new_hash)
end
h = { 'b' => 2 }
copy(h)
h == { 'a' => 1 } # => true
v = { "foo"=>"bar"}
v["foo"] // bar
Say,
k = {:bar => 1}
>k[:bar] // 1
But,
k[v["foo"]] // nil
how to access key ==> value ( k[ key ] = value ) from hashes using a variable, say v["foo"]
You'll need to run to_sym on the result of v["foo"] in order to the the value in k:
1.9.3p484 :007 > v = { "foo"=>"bar"}
=> {"foo"=>"bar"}
1.9.3p484 :008 > k = {:bar => 1}
=> {:bar=>1}
1.9.3p484 :009 > k[v["foo"].to_sym]
=> 1
I've the following Hash (dummy{}) with one member being an array, how do I build a code logic to:
(a) evaluate each Key -> Value
(b) evaluate each Key -> values(s) (if it is an array inside a Hash)
dummy = { :a1 => "xyz",
:b1 => ["xyz1", "ayz2", "xyz3", "xyz4"] }
The code I've for a Key with just one value is:
eval(dummy)[a1.to_sym]
I need a clean way to evaluate a hash member that has multiple values in an array.
Here is the IRB output:
1.9.3-p327 :002 > dummy = { :a1 => "xyz",
1.9.3-p327 :003 > :b1 => ["xyz1", "ayz2", "xyz3", "xyz4"] }
=> {:a1=>"xyz", :b1=>["xyz1", "ayz2", "xyz3", "xyz4"]}
Now I can access the members and their Key-Value pairs (in a very simple way as below:)
1.9.3-p327 :005 > pp dummy[:a1.to_sym]
"xyz"
=> "xyz"
1.9.3-p327 :006 > pp dummy[:b1.to_sym][0]
"xyz1"
=> "xyz1"
1.9.3-p327 :007 > pp dummy[:b1.to_sym][1]
"ayz2"
=> "ayz2"
1.9.3-p327 :008 > pp dummy[:b1.to_sym][2]
"xyz3"
=> "xyz3"
1.9.3-p327 :009 > pp dummy[:b1.to_sym][3]
"xyz4"
=> "xyz4"
Now, I need a "generic" ruby code that takes care of both the above situations to access the members and their values - note: for example, I only selected 1 and 4 values, but in reality my need is where the values range from 1000 - 5000
Making sure everything is an Array allows for uniform treatment:
dummy = { :a1 => "xyz",
:b1 => ["xyz1", "ayz2", "xyz3", "xyz4"] }
dummy.each do |k, v|
Array(v).each do |element| #Array(v) puts v in an Array, unless it already is an array.
puts element
end
puts
end
I don't understand how Ruby hashes work.
I expect these:
a = 'a'
{a => 1}[a] # => 1
{a: 1}[:a] # => 1
{2 => 1}[2] # => 1
How does this work?
{'a' => 1}['a'] # => 1
The first string 'a' is not the same object as the second string 'a'.
Ruby doesn't use object equality (equal?) for comparing hash keys. It wouldn't be very useful if it did after all.
Instead it uses eql?, which for strings is the same as ==
As a footnote to other answers, you can let a hash behave like you expected:
h = {'a'=> 1}
p h['a'] #=> 1
h.compare_by_identity
p h['a'] #=> nil ; not the same object
some_hash[k] = v
Basically, when you do this, what is stored is not a direct association k => v. Instead of that, k is asked for a hash code, which is then used to map to v.
Equal values yield equal hash codes. That's why your last example works the way it does.
A couple of examples:
1.9.3p0 :001 > s = 'string'
=> "string"
1.9.3p0 :002 > 'string'.hash
=> -895223107629439507
1.9.3p0 :003 > 'string'.hash == s.hash
=> true
1.9.3p0 :004 > 2.hash
=> 2271355725836199018
1.9.3p0 :005 > nil.hash
=> 2199521878082658865
This question already has answers here:
Strange, unexpected behavior (disappearing/changing values) when using Hash default value, e.g. Hash.new([])
(4 answers)
Closed 7 years ago.
Code example below. Calling append on a hash value returns correctly but the hash itself doesn't behave as I would expect.
ruby-1.9.2-p290 :037 > r = {}
=> {}
ruby-1.9.2-p290 :038 > r.default = []
=> []
ruby-1.9.2-p290 :039 > r["c"] << 1
=> [1]
ruby-1.9.2-p290 :040 > r["c"]
=> [1]
ruby-1.9.2-p290 :041 > r
=> {}
ruby-1.9.2-p290 :042 > r.empty?
=> true
from the doc on default=:
Sets the default value, the value returned for a key that does not
exist in the hash. It is not possible to set the default to a Proc
that will be executed on each key lookup.
So you can use this:
irb(main):037:0> h = Hash.new { |hash, key| hash[key] = [] }
=> {}
irb(main):038:0> h[1] << "2"
=> ["2"]
irb(main):039:0> h
=> {1=>["2"]}
also you can use default_proc:
irb(main):047:0> h = {}
irb(main):047:0> h.default_proc = proc {|hash, key| hash[key] = []}
=> #<Proc:0x23939a0#(irb):47>
irb(main):049:0> h[1] << 3
=> [3]
irb(main):050:0> h
=> {1=>[3]}
It seems ruby hash uses r.default as default value for any r[<unassigned-key>]. So a single value is changed even if you specify different un-assigned keys. Please see below:
irb(main):001:0> r = {}
=> {}
irb(main):002:0> r.default = []
=> []
irb(main):003:0> r["c"] << 1
=> [1]
irb(main):004:0> r["c"]
=> [1]
irb(main):005:0> r["b"] << 2
=> [1, 2]
irb(main):006:0> r["b"]
=> [1, 2]
irb(main):007:0> r["c"]
=> [1, 2]
irb(main):010:0> r.default
=> [1, 2]
irb(main):008:0> r
=> {}
irb(main):009:0> r.empty?
=> true