Hash appears to be passing by reference : Ruby - ruby

I know Title is ambiguous, so lets look in to below example.
I have a Hash named sh , then I assign sh to a new variable su , after that I am modifying sh , but su also getting modified . I want to keep su with original content.
irb(main):001:0> sh = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}
=> {"name"=>"shan", "age"=>"33", "sex"=>"male"}
irb(main):002:0> su = sh
=> {"name"=>"shan", "age"=>"33", "sex"=>"male"}
irb(main):003:0> su
=> {"name"=>"shan", "age"=>"33", "sex"=>"male"}
irb(main):005:0> sh.delete("sex")
=> "male"
irb(main):006:0> sh
=> {"name"=>"shan", "age"=>"33"} => ok here
irb(main):007:0> su
=> {"name"=>"shan", "age"=>"33"} => ??
irb(main):010:0> sh["city"] = "Bangalore" => New example
=> "Bangalore"
irb(main):011:0> sh
=> {"name"=>"shan", "age"=>"33", "city"=>"Bangalore"} => ok
irb(main):012:0> su
=> {"name"=>"shan", "age"=>"33", "city"=>"Bangalore"} => ??

This is not a problem if passing by reference or value. It is just two variables pointing to the same object and that object can be modified (adding keys, removing keys, altering values).
You can check this by using object_id
3.0.0 :002 > sh.object_id
=> 260
3.0.0 :004 > su.object_id
=> 260
It is this line in your program
su = sh
which tells Ruby that you want the Hash that you call sh also to be known as su. Kind of calling me by name or nickname:-)
Not that the output of object_id might differ between ruby versions and runs of your program.
There are various ways to have two separate hashes (or other objects:
Create two separate objects with the initializer:
sh = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}
su = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}
Create a clone:
sh = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}
su = sh.clone
#clone is defined for the standard ruby classes (e.g. Strings and Numbers). If you store something other in your Hash, then you might need to implement it on your own to get a correct copy.
3.0.0 :005 > su = sh.clone
3.0.0 :006 > sh.object_id
=> 260
3.0.0 :007 > su.object_id
=> 280
Now it gets interesting though. You only cloned the Hash, not the objects that you have stored inside it:
sh = {a: "Hi"}
su = sh.clone
3.0.0 :010 > sh[:a].object_id
=> 300
3.0.0 :011 > su[:a].object_id
=> 300
that means if you modify an object inside your hash, it will also change the object that is referenced by the other hash:
Depending on what you want, you can assign new objects:
sh = {a: "Hi"}
su = sh.clone
sh[:a] = "HI
sh[:a]
# => "HI"
su[:a]
# => "Hi"
or modify them inplace
sh = {a: "Hi"}
su = sh.clone
sh[:a].upcase! # this changes the string inplace
sh[:a]
# => "HI"
su[:a]
# => "HI"
clone makes a shallow copy, if you want to have a complete copy, where clone is called on every object you can do the Marshal load/dump trick described by Denny Mueller or look into ActiveSupports #deep_dup https://guides.rubyonrails.org/active_support_core_extensions.html#deep-dup
Or you can write an implementation of this on your own and add it to your hash.
(there are also some gems out there)

The clone method is Ruby's standard, built-in way to do a shallow-copy:
su = sh.clone
Remark: the above solution is only for non nested hashes. In case of nested hashed you need to marshal
su = Marshal.load(Marshal.dump(sh))

.delete remove data from memory
you can use this: su = Hash[sh]

Related

Why a dangerous method doesn't work with a character element of String in Ruby?

When I apply the upcase! method I get:
a="hello"
a.upcase!
a # Shows "HELLO"
But in this other case:
b="hello"
b[0].upcase!
b[0] # Shows h
b # Shows hello
I don't understand why the upcase! applied to b[0] doesn't have any efect.
b[0] returns a new String every time. Check out the object id:
b = 'hello'
# => "hello"
b[0].object_id
# => 1640520
b[0].object_id
# => 25290780
b[0].object_id
# => 24940620
When you are selecting an individual character in a string, you're not referencing the specific character, you're calling a accessor/mutator function which performs the evaluation:
2.0.0-p643 :001 > hello = "ruby"
=> "ruby"
2.0.0-p643 :002 > hello[0] = "R"
=> "R"
2.0.0-p643 :003 > hello
=> "Ruby"
In the case when you run a dangerous method, the value is requested by the accessor, then it's manipulated and the new variable is updated, but because there is no longer a connection between the character and the string, it will not update the reference.
2.0.0-p643 :004 > hello = "ruby"
=> "ruby"
2.0.0-p643 :005 > hello[0].upcase!
=> "R"
2.0.0-p643 :006 > hello
=> "ruby"

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.

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.

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

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

Ruby equivalent of Perl Data::Dumper

I am learning Ruby & Perl has this very convenient module called Data::Dumper, which allows you to recursively analyze a data structure (like hash) & allow you to print it. This is very useful while debugging. Is there some thing similar for Ruby?
Look into pp
example:
require 'pp'
x = { :a => [1,2,3, {:foo => bar}]}
pp x
there is also the inspect method which also works quite nicely
x = { :a => [1,2,3, {:foo => bar}]}
puts x.inspect
I normally use a YAML dump if I need to quickly check something.
In irb the syntax is simply y obj_to_inspect. In a normal Ruby app, you may need to add a require 'YAML' to the file, not sure.
Here is an example in irb:
>> my_hash = {:array => [0,2,5,6], :sub_hash => {:a => 1, :b => 2}, :visible => true}
=> {:sub_hash=>{:b=>2, :a=>1}, :visible=>true, :array=>[0, 2, 5, 6]}
>> y my_hash # <----- THE IMPORTANT LINE
---
:sub_hash:
:b: 2
:a: 1
:visible: true
:array:
- 0
- 2
- 5
- 6
=> nil
>>
The final => nil just means the method didn't return anything. It has nothing to do with your data structure.
you can use Marshal, amarshal, YAML

Resources