Modifying a hash with multiple occurrences [duplicate] - ruby

This question already has answers here:
How do I copy a hash in Ruby?
(13 answers)
Closed 8 years ago.
a is a hash. s is an array where I want to push the hash a iteratively. The idea is to retain the value of each iteratively pushed hash independently. Here is what I tried.
a = {:a=> 1, :b=>2}
s = []
s << a
s << a # => [{:b=>2, :a=>1}, {:b=>2, :a=>1}]
a[:b] = 3
s # => [{:b=>3, :a=>1}, {:b=>3, :a=>1}]
t = []
t.push(a) # => [{:b=>3, :a=>1}]
t.push(a) # => [{:b=>3, :a=>1}, {:b=>3, :a=>1}]
a[:b] = 4
t # => [{:b=>4, :a=>1}, {:b=>4, :a=>1}]
The above doesn't give me the solution: Changing a changes the values in the elements inside the array which were previously pushed. After pushing a to s twice, I changed a[:b] from 2 to 3, and all the elements reflected the change. Suggestion for this please.

Use dup every time you're adding to s
s << a.dup
The dup method will create shallow copy of hash.
Update:
In case if the shallow copy doesn't satisfy the requirements, then use Marshaling
s << Marshal.load( Marshal.dump(a) )

Use
s << Hash[a]
This will copy the Hash and allow you to change the original.

Related

Return a default value of a hash [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I need help. I am doing a basic exercise in Ruby, but right now I don't understand defult that much.
I need to write a function called temp that reicive one argument (a hash). The function must return the same hash but with the next changes:
If the hash doesn't have the key :puppy return 4 as value.
If the hash doesn't have the key :cat return 6 as value.
Assuming this question is about default values, you're after either default or default_proc. You can make a new hash and give it a default proc via Hash.new.
def temp(h)
Hash.new do |hash, key|
if key == :puppy
h.fetch(:puppy, 4)
elsif key == :cat
h.fetch(:cat, 6)
else
h[key]
end
end
end
While Hash#default_proc is the right way to go, both answers here are overcomplicated for this particular task. The following is probably what you actually need here.
def temp(h)
h[:puppy] ||= 4
h[:cat] ||= 6
h
end
Your question is a little confusing.
If the hash doesn't have the key :puppy return 4 as value.
If the hash doesn't have the key :cat return 6 as value.
I think you want a function that returns a version of the passed-in hash that defaults the keys :puppy and :cat to 4 and 6, respectively, assuming they aren't already in the passed-in hash. This should work:
def temp(source)
return Hash.new do |h, k|
case k
when :puppy then 4
when :cat then 6
end
end.merge(source)
end
Here's how this works.
We're defining a method that takes as its one parameter a basis hash (which I called source).
When you create a hash with Hash.new and pass in a block of code, that block becomes the hash's "default proc". It will be called whenever a key that is not present in the hash is looked up, and its return value will appear to be the value stored at that key. When such a lookup happens, the block is passed two arguments: the hash itself and the key being requested. Here, I named those h and k. In the body, we check to see if k is either :puppy or :cat and return the appropriate value (4 or 6) if so. (If k is not either of those values, the case statement will automatically return nil.)
That creates a hash with the right default proc, but the hash is empty. We then populate it by calling Hash#merge on it with the passed-in source hash as argument; that will copy all of the keys from source into the new hash. The merge method call conveniently returns the hash it was called on, so we just return that result to the caller of our method.
Examples:
irb(main):001:0> h1 = temp( { a: 1, b: 2, c: 3 } )
=> {:a=>1, :b=>2, :c=>3}
irb(main):002:0> h1[:a]
=> 1
irb(main):003:0> h1[:d]
=> nil
irb(main):004:0> h1[:cat]
=> 6
irb(main):005:0> h1[:puppy]
=> 4
irb(main):006:0> h2 = temp( { a: 1, cat: :dog } )
=> {:a=>1, :cat=>:dog}
irb(main):007:0> h2[:a]
=> 1
irb(main):008:0> h2[:b]
=> nil
irb(main):009:0> h2[:cat]
=> :dog
irb(main):010:0> h2[:puppy]
=> 4

Ruby Hash of Arrays outputing {} [duplicate]

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.
If I do the following:
h = Hash.new(Array.new)
h['a'].push('apple')
puts h['a']
puts h
I get the following output:
apple
{}
I don't understand why puts h isn't outputing:
{"a"=>["apple"]}
Any help much appreciated...
Read this
new(obj) → new_hash
If obj is specified, this single object will be used for all default values.
Hash.new(Array.new) - by this line you have created a default array object. Which will be returned whenever you want to access a key, which not exist inside the hash.
h['a'].push('apple') - By this line, you actually adding/pushing a value to that default array object,but not adding any key to the hash. h['a'] is returning you the array you defined with Array.new, on that you are calling Array#push, that's all.Thus h['a'] is giving you the current content of that default array. puts h ofcourse will give you {}, as you didn't added the key 'a' to the hash.
See the same in action :
h = Hash.new(Array.new)
h.default # => []
h['a'].push('apple')
h.default # => ["apple"]
Now look the code again :
#adding a key
h['a'] = 'Bob'
h['a'] # => "Bob"
h # => {"a"=>"Bob"}
#again default vaule as you are trying to aceess a non-exist key
h['b'] # => ["apple"]
Worth to read Hash#[]
Element Reference— Retrieves the value object corresponding to the key object. If not found, returns the default value (see Hash::new for details).

Cloning a Hash in Ruby2 [duplicate]

This question already has answers here:
How to create a deep copy of an object in Ruby?
(9 answers)
Closed 8 years ago.
Im trying to clone a hash, to make a new copy of the original hash but it seems that when I set a value in the new hash, I have the same effect on the original hash.
rr = Hash.new
command = "/usr/local/bin/aws route53 list-resource-record-sets --hosted-zone-id EXAMPLEID --max-items 1"
rr=JSON.parse(%x{#{command}})
puts rr
if rr["ResourceRecordSets"][0]["TTL"] != 60
new_rr = rr.clone
new_rr["ResourceRecordSets"][0]["TTL"] = 60
puts rr
puts new_rr
end
Output:
{"NextRecordType"=>"MX", "NextRecordName"=>"example.com.", "ResourceRecordSets"=>[{"ResourceRecords"=>[{"Value"=>"1.2.3.4"}], "Type"=>"A", "Name"=>"example.com.", "TTL"=>1800}], "MaxItems"=>"1", "IsTruncated"=>true}
{"NextRecordType"=>"MX", "NextRecordName"=>"example.com.", "ResourceRecordSets"=>[{"ResourceRecords"=>[{"Value"=>"1.2.3.4"}], "Type"=>"A", "Name"=>"example.com.", "TTL"=>60}], "MaxItems"=>"1", "IsTruncated"=>true}
{"NextRecordType"=>"MX", "NextRecordName"=>"example.com.", "ResourceRecordSets"=>[{"ResourceRecords"=>[{"Value"=>"1.2.3.4"}], "Type"=>"A", "Name"=>"example.com.", "TTL"=>60}], "MaxItems"=>"1", "IsTruncated"=>true}
I dont see Hash.clone documented in Ruby 2.0, should I be using another method to create a Hash copy now?
Thanks in advance.
Hash is a collection of keys and values, where values are references to objects. When duplicating a hash, new hash is being created, but all object references are being copied, so as result you get new hash containing the same values. That is why this will work:
hash = {1 => 'Some string'} #Strings are mutable
hash2 = hash.clone
hash2[1] #=> 'Some string'
hash2[1].upcase! # modifying mutual object
hash[1] #=> 'SOME STRING; # so it appears modified on both hashes
hash2[1] = 'Other string' # changing reference on second hash to another object
hash[1] #=> 'SOME STRING' # original obejct has not been changed
hash2[2] = 'new value' # adding obejct to original hash
hash[2] #=> nil
If you want duplicate the referenced objects, you need to perform deep duplication. It is added in rails (activesupport gem) as deep_dup method. If you are not using rails and don;t want to install the gem, you can write it like:
class Hash
def deep_dup
Hash[map {|key, value| [key, value.respond_to?(:deep_dup) ? value.deep_dup : begin
value.dup
rescue
value
end]}]
end
end
hash = {1 => 'Some string'} #Strings are mutable
hash2 = hash.deep_dup
hash2[1] #=> 'Some string'
hash2[1].upcase! # modifying referenced object
hash2[1] #=> 'SOME STRING'
hash[1] #=> 'Some string; # now other hash point to original object's clone
You probably should write something similar for arrays. I would also thought about writing it for whole enumerable module, but it might be slightly trickier.
The easiest way to make a deep copy of most Ruby objects (including strings, arrays, hashes and combinations thereof) is to use Marshal:
def deep_copy(obj)
Marshal.load(Marshal.dump(obj))
end
For example,
h = {a: 1, b: [:c, d: {e: 4}]} # => {:a=>1, :b=>[:c, {:d=>{:e=>4}}]}
hclone = h.clone
hdup = h.dup
hmarshal = deep_copy(h)
h[:b][1][:d][:e] = 5
h # => {:a=>1, :b=>[:c, {:d=>{:e=>5}}]}
hclone # => {:a=>1, :b=>[:c, {:d=>{:e=>5}}]}
hdup # => {:a=>1, :b=>[:c, {:d=>{:e=>5}}]}
hmarshal # => {:a=>1, :b=>[:c, {:d=>{:e=>4}}]}

What are the differences between "each", "foreach", "collect" and "map"? [duplicate]

This question already has answers here:
what's different between each and collect method in Ruby [duplicate]
(7 answers)
Closed 8 years ago.
It seems like there are a lot of ways to loop over an Enumerable in Ruby. Are there any differences between each, foreach, or in collect, map or other similar methods?
Or is there a reason I shouldn't use certain methods in certain situations?
collect/map are equivalent. They differ from each in that each only executes the block for each element, whereas collect/map return an array with the results of calling the block for each element. Another way to put it might be, each is expected to do something with each element, whereas map is expected to transform each element (map it onto something else).
You could use collect or map anywhere each is used, and your code will still work. But it will probably be slightly less efficient because it collects the results in an array, unless your Ruby implementation realizes it doesn't have to bother creating an array because it's never used.
Another reason to use each instead of map or collect is to help out anyone reading your code. If I see each then I can be like okay, we're about to use each element of the data to do something. If I see map then I'm expecting to see new data being created, based on a remapping of the old data.
With regards to map vs. collect I would say it's a matter of preference, but you should pick one and stick with it for consistency.
Using pry and Rubinus (it's easier to read ruby code) take a look for yourself
$ pry
[1] pry(main)> cd Enumerable
[2] pry(Enumerable):1> show-method collect
From: .../rbx-head/kernel/common/enumerable18.rb # line 4:
Owner: Enumerable
Visibility: public
Number of lines: 9
def collect
if block_given?
ary = []
each { |o| ary << yield(o) }
ary
else
to_a
end
end
[3] pry(Enumerable):1> show-method map
From: .../rbx-head/kernel/common/enumerable18.rb # line 4:
Owner: Enumerable
Visibility: public
Number of lines: 9
def collect
if block_given?
ary = []
each { |o| ary << yield(o) }
ary
else
to_a
end
end
[4] pry(Enumerable):1>
So nope not for these two.
map and collect iterate over a collection. For each object it executes your block then collects the result. The each methods do not collect the results
Array#collect (and Array#map) return a new array based on the code passed in the block. Array#each performs an operation (defined by the block) on each element of the array.
I would use collect like this:
array = [1, 2, 3]
array2 = array.collect {|val| val + 1}
array.inspect # => "[1, 2, 3]"
array2.inspect # => "[2, 3, 4]"
And each like this:
array = [1, 2, 3]
array.each {|val| puts val + 1 }
# >> 2
# >> 3
# >> 4
array.inspect # => "[1, 2, 3]"

Is this correct behaviour for a Ruby hash with a default value? [duplicate]

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.
hash = Hash.new(Hash.new([]))
hash[1][2] << 3
hash[1][2] # => [3]
hash # => {}
hash.keys # => []
hash.values # => []
What's going on? Ruby's hiding data (1.9.3p125)
What's going on? Ruby's hiding data (1.9.3p125)
Ruby hides neither data nor its docs.
Default value you pass into the Hash constructor is returned whenever the key is not found in the hash. But this default value is never actually stored into the hash on its own.
To get what you want you should use Hash constructor with block and store default value into the hash yourself (on both levels of your nested hash):
hash = Hash.new { |hash, key| hash[key] = Hash.new { |h, k| h[k] = [] } }
hash[1][2] << 3
p hash[1][2] #=> [3]
p hash #=> {1=>{2=>[3]}}
p hash.keys #=> [1]
p hash.values #=> [{2=>[3]}]
It's simple. If you pass an object to a Hash constructor, it'll become a default value for all missing keys in that hash. What's interesting is that this value is mutable. Observe:
hash = Hash.new(Hash.new([]))
# mutate default value for nested hash
hash[1][2] << 3
# default value in action
hash[1][2] # => [3]
# and again
hash[1][3] # => [3]
# and again
hash[1][4] # => [3]
# set a plain hash (without default value)
hash[1] = {}
# what? Where did my [3] go?
hash[1][2] # => nil
# ah, here it is!
hash[2][3] # => [3]
I get a try with this in irb.
Seams Ruby does not tag element as "visible" except affecting value over default explicitly via = for instance.
hash = Hash.new(Hash.new([]))
hash[1] = Hash.new([])
hash[1][2] = [3]
hash
#=> {1=>{2=>[3]}}
May be some setters are missing this "undefaulting" behavior ...

Resources