Why does clearing my hash, also clear my array of hashes? - ruby

ruby-1.9.2-p180 :154 > a = []
=> []
ruby-1.9.2-p180 :154 > h = {:test => "test"}
=> {:test=>"test"}
ruby-1.9.2-p180 :155 > a << h
=> [{:test=>"test"}]
ruby-1.9.2-p180 :156 > h.clear
=> {}
ruby-1.9.2-p180 :157 > a
=> [{}]
I'm very confused, especially since I can change the elements of the hash without it affecting the array. But when I clear the hash the array is updated and cleared of its hash contents. Can someone explain?

When you do a << h, you are really passing the reference of h to a. So when you update h, a also see's those changes because it contains a reference rather than a copy of that value.
In order for it not to change in a, you must pass a cloned value of h into a.
An example would be:
a << h.clone

Ruby does not make a copy of this hash when you add it to the array — it simply stores a reference to the original variable. So, when you empty the original variable, the reference stored in the array now refers to the empty hash.
If you want to copy the hash element so this does not occur, use Ruby's clone method.
ruby-1.9.2-p136 :049 > h = { :test => 'foo' }
=> {:test=>"foo"}
ruby-1.9.2-p136 :050 > a = []
=> []
ruby-1.9.2-p136 :051 > a << h.clone
=> [{:test=>"foo"}]
ruby-1.9.2-p136 :052 > h.clear
=> {}
ruby-1.9.2-p136 :053 > a
=> [{:test=>"foo"}]

Related

How do I copy contents into a hash without changing the memory address to which that hash points?

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

IRB (apparently) not inspecting hashes correctly

I'm seeing some odd behavior in IRB 1.8.7 with printing hashes. If I initialize my hash with a Hash.new, it appears that my hash is "evaluating" to an empty hash:
irb(main):024:0> h = Hash.new([])
=> {}
irb(main):025:0> h["test"]
=> []
irb(main):026:0> h["test"] << "blah"
=> ["blah"]
irb(main):027:0> h
=> {}
irb(main):028:0> puts h.inspect
{}
=> nil
irb(main):031:0> require 'pp'
=> true
irb(main):032:0> pp h
{}
=> nil
irb(main):033:0> h["test"]
=> ["blah"]
As you can see, the data is actually present in the hash, but trying to print or display it seems to fail. Initialization with a hash literal seems to fix this problem:
irb(main):050:0> hash = { 'test' => ['testval'] }
=> {"test"=>["testval"]}
irb(main):051:0> hash
=> {"test"=>["testval"]}
irb(main):053:0> hash['othertest'] = ['secondval']
=> ["secondval"]
irb(main):054:0> hash
=> {"othertest"=>["secondval"], "test"=>["testval"]}
The issue here is that invoking h["test"] doesn't actually insert a new key into the hash - it just returns the default value, which is the array that you passed to Hash.new.
1.8.7 :010 > a = []
=> []
1.8.7 :011 > a.object_id
=> 70338238506580
1.8.7 :012 > h = Hash.new(a)
=> {}
1.8.7 :013 > h["test"].object_id
=> 70338238506580
1.8.7 :014 > h["test"] << "blah"
=> ["blah"]
1.8.7 :015 > h.keys
=> []
1.8.7 :016 > h["bogus"]
=> ["blah"]
1.8.7 :017 > h["bogus"].object_id
=> 70338238506580
1.8.7 :019 > a
=> ["blah"]
The hash itself is still empty - you haven't assigned anything to it. The data isn't present in the hash - it's present in the array that is returned for missing keys in the hash.
It looks like you're trying to create a hash of arrays. To do so, I recommend you initialize like so:
h = Hash.new { |h,k| h[k] = [] }
Your version isn't working correctly for me, either. The reason why is a little complicated to understand. From the docs:
If obj is specified, this single object will be used for all default values.
I've added the bolding. The rest of the emphasis is as-is.
You're specifying that obj is [], and it's only a default value. It doesn't actually set the contents of the hash to that default value. So when you do h["blah"] << "test", you're really just asking it to return a copy of the default value and then adding "test" to that copy. It never goes into the hash at all. (I need to give Chris Heald credit for explaining this below.)
If instead you give it a block, it calls that block EVERY TIME you do a lookup on a non-existent entry of the hash. So you're not just creating one Array anymore. You're creating one for each entry of the hash.

How to evaluate a member inside a hash that is a list

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

Finding out variable class and difference between Hash and Array

Is it possible to clearly identify a class of variable?
something like:
#users.who_r_u? #=>Class (some information)
#packs.who_r_u? #=> Array (some information)
etc.
Can someone provide clear short explanation of difference between Class, Hash, Array, Associated Array, etc. ?
You can use:
#users.class
Test it in irb:
1.9.3p0 :001 > 1.class
=> Fixnum
1.9.3p0 :002 > "1".class
=> String
1.9.3p0 :003 > [1].class
=> Array
1.9.3p0 :004 > {:a => 1}.class
=> Hash
1.9.3p0 :005 > (1..10).class
=> Range
Or:
1.9.3p0 :010 > class User
1.9.3p0 :011?> end
=> nil
1.9.3p0 :012 > #user = User.new
=> #<User:0x0000010111bfc8>
1.9.3p0 :013 > #user.class
=> User
These were only quick irb examples, hope it's enough to see the use of .class in ruby.
You could also use kind_of? to test wheter its receiver is a class, an array or anything else.
#users.kind_of?(Array) # => true
You can find these methods in Ruby document http://ruby-doc.org/core-1.9.3/Object.html
#user.class => User
#user.is_a?(User) => true
#user.kind_of?(User) => true
found helpful: <%= debug #users %>
A difference between Class and Hash? They are too different to even provide normal answer. Hash is basically an array with unique keys, where each key has its associated value. That's why it's also called associative array.
Here is some explanation:
array = [1,2,3,4]
array[0] # => 1
array[-1] # => 4
array[0..2] # => [1,2,3]
array.size # => 4
Check out more Array methods here: http://ruby-doc.org/core-1.9.3/Array.html
hash = {:foo => 1, :bar => 34, :baz => 22}
hash[:foo] # => 1
hash[:bar] # => 34
hash.keys # => [:baz,:foo,:bar]
hash.values # => [34,22,1]
hash.merge :foo => 3921
hash # => {:bar => 34,:foo => 3921,:baz => 22 }
Hash never keeps order of the elments you added to it, it just preserves uniqueness of keys, so you can easily retreive values.
However, if you do this:
hash.merge "foo" => 12
you will get
hash # => {:bar => 34, baz => 22, "foo" => 12, :foo => 2}
It created new key-value pair since :foo.eql? "foo" returns false.
For more Hash methods check out this: http://www.ruby-doc.org/core-1.9.3/Hash.html
Class object is a bit too complex to explain in short, but if you want to learn more about it, reffer to some online tutorials.
And remember, API is your friend.

What are values mapped to?

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

Resources