Changing multidimensional hash value and it's still equal - ruby

I'm trying to update a multidimensinal hash and then compare the new hash with the old one, but no matter what value I change they still show up as equal. Is there an efficient way to compare every value in two hashes and return false if one isn't true
hash1 = {foo: {bar: "baz"}}
hash2 = hash1
hash2[:foo][:bar] = "foz"
hash2 == hash1 # This should be false but is returning true

Unlike assigning to simple primitive objects like integers, when you assign a variable to a hash, it will point to the same hash object.
So, hash2 is pointing on the same object as hash1 in memory.
If you need them to be structurally the same but are different objects, you need to copy the first hash deeply.
def deep_copy(hash)
Marshal.load(Marshal.dump(hash))
end
hash1 = {foo: {bar: "baz"}}
hash2 = deep_copy(hash1)
hash2[:foo][:bar] = "foz"
hash2 == hash1

Related

compute hash value of same key ruby

I start with a basic hash where the key is a string and value an integer.
hash = {"a"=>2, "b"=>3}
Then what I try to achieve is that i want to push several times into that hash a new hash with different keys or / and same :
hash2 = {"c"=>4, "a"=>5}
The result should be
h_result = {"a"=>7, "b"=>3, "c"=>4}
The first thing would be to push the new hash and keep the duplicate keys.
I saw that answer = How can I merge two hashes without overwritten duplicate keys in Ruby? but it seems that it's not working..
Then I think I should match the same keys and compute the values. But again I can't find the answer.
Thanks guys
If you just want to compute equal keys in the hash what you are looking for is the merge method in the Hash class.
https://ruby-doc.org/core-2.2.1/Hash.html#method-i-merge
Returns a new hash containing the contents of other_hash and the
contents of hsh. If no block is specified, the value for entries with
duplicate keys will be that of other_hash. Otherwise the value for
each duplicate key is determined by calling the block with the key,
its value in hsh and its value in other_hash.
When you pass a block to the merge method it will yield both old value and new value, and the you can do your computation there.
For instance:
hash = {"a"=>2, "b"=>3}
hash2 = {"c"=>4, "a"=>5}
result = hash.merge(hash2) { |key, old_val, new_val| old_val + new_val }
p result #=> {"a"=>7, "b"=>3, "c"=>4}
Just use Hash#merge with a block and tell Ruby what to do when the key exists in both hashes – in this example just add the value from the second hash to the value from the first hash.
hash.merge(hash2) { |key, v1, v2| v1 + v2 }
#=> { "a" => 7, "b" => 3, "c" => 4 }

Detecting if a key-value pair exists within a hash

I cannot find a way to determine if a key-value pair exists in a hash.
h4 = { "a" => 1, "d" => 2, "f" => 35 }
I can use Hash#has_value? and Hash#has_key? to find information about a key or a value individually, but how can I check if a pair exists?
Psuedo-code of what I'm after:
if h4.contains_pair?("a", 1)
Just use this:
h4['a'] == 1
It seems excessive to me, but you could monkey-patch Hash with a method like so:
class Hash
def contains_pair?(key, value)
key?(key) && self[key] == value
end
end
I confess to starting down a road and then wondering where it might take me. This may not be the best way of determining if a key/value pair is present in a hash (how could one improve on #Jordan's answer?), but I learned something along the way.
Code
def pair_present?(h,k,v)
Enumerable.instance_method(:include?).bind(h).call([k,v])
end
Examples
h = { "a"=>1, "d"=>2, "f"=>35 }
pair_present?(h,'a',1)
#=> true
pair_present?(h,'f',36)
#=> false
pair_present?(h,'hippopotamus',2)
#=> false
Discussion
We could of course convert the hash to an array and then apply Array#include?:
h.to_a.include?(['a', 1])
#=> true
but that has the downside of creating a temporary array. It would be nice if the class Hash had such an instance method, but it does not. One might think Hash#include? might be used for that, but it just takes one argument, a key.1.
The method Enumerable#include? does what we want, and of course Hash includes the Enumerable module. We can invoke that method in various ways.
Bind Enumerable#include? to the hash and call it
This was of course my answer:
Enumerable.instance_method(:include?).bind(h).call([k,v])
Use the method Method#super_method, which was introduced in v2.2.
h.method(:include?).super_method.call(['a',1])
#=> true
h.method(:include?).super_method.call(['a',2])
#=> false
Note that:
h.method(:include?).super_method
#=> #<Method: Object(Enumerable)#include?>
Do the alias_method/remove_method merry-go-round
Hash.send(:alias_method, :temp, :include?)
Hash.send(:remove_method, :include?)
h.include?(['a',1])
#=> true
h.include?(['a',2])
#=> false
Hash.send(:alias_method, :include?, :temp)
Hash.send(:remove_method, :temp)
Convert the hash to an enumerator and invoke Enumerable#include?
h.to_enum.include?(['a',1])
#=> true
h.to_enum.include?(['a',2])
#=> false
This works because the class Enumerator also includes Enumerable.
1 Hash#include? is the same as both Hash#key? and Hash#has_key?. It makes me wonder why include? isn't used for the present purpose, since determining if a hash has a given key is well-covered.
How about using Enumerable any?
h4 = { "a" => 1, "d" => 2, "f" => 35 }
h4.any? {|k,v| k == 'a' && v == 1 }

What is the difference between initializing hash two times and clear?

Suppose I have an hash = {1=>1,2=>2} to clear the values I can do either hash = {} or hash.clear . What is the difference between them?
According to the docs, if you see the source code for clear method, it iterates through each element in the hash and removes it.
When you do hash = {}, it will just create a new empty hash object but the old object will still be in memory ready for garbage collection if there are no other references for that object.
They are completely different.
The first one will simply bind the local variable hash to a different object. In particular, it will not "clear the values", as you claim:
hash = {i: 'am', still: 'there'}
another = hash
hash = {}
another
# => {i: 'am', still: 'there'}
The second one will actually clear the values:
hash = {i: 'am', still: 'there'}
another = hash
hash.clear
another
# => {}
Atri's answer is right, but beyond the Hash still existing in memory for GC when you use reassignment -- is if the array is still actually referenced by another variable.
A Hash is an object, a variable is just a pointer or reference to it. #clear clears the object itself, reassigning the variable just points the variable to a new Hash.
hash1 = {1=>1,2=>2}
hash2 = hash1
puts hash1 # => {1=>1,2=>2}
puts hash2 # => {1=>1,2=>2}
hash1 = {}
puts hash1 # => {}
puts hash2 # => {1=>1,2=>2}
hash1 = {1=>1,2=>2}
hash2 = hash1
hash1.clear
puts hash1 # => {}
puts hash2 # => {}

Ruby how to return an element of a dictionary?

# dictionary = {"cat"=>"Sam"}
This a return a key
#dictionary.key(x)
This returns a value
#dictionary[x]
How do I return the entire element
"cat"=>"Sam"
#dictionary
should do the trick for you
whatever is the last evaluated expression in ruby is the return value of a method.
If you want to return the hash as a whole. the last line of the method should look like the line I have written above
Your example is a bit (?) misleading in a sense it only has one pair (while not necessarily), and you want to get one pair. What you call a "dictionary" is actually a hashmap (called a hash among Rubyists).
A hashrocket (=>) is a part of hash definition syntax. It can't be used outside it. That is, you can't get just one pair without constructing a new hash. So, a new such pair would look as: { key => value }.
So in order to do that, you'll need a key and a value in context of your code somewhere. And you've specified ways to get both if you have one. If you only have a value, then:
{ #dictionary.key(x) => x }
...and if just a key, then:
{ x => #dictionary[x] }
...but there is no practical need for this. If you want to process each pair in a hash, use an iterator to feed each pair into some code as an argument list:
#dictionary.each do |key, value|
# do stuff with key and value
end
This way a block of code will get each pair in a hash once.
If you want to get not a hash, but pairs of elements it's constructed of, you can convert your hash to an array:
#dictionary.to_a
# => [["cat", "Sam"]]
# Note the double braces! And see below.
# Let's say we have this:
#dictionary2 = { 1 => 2, 3 => 4}
#dictionary2[1]
# => 2
#dictionary2.to_a
# => [[1, 2], [3, 4]]
# Now double braces make sense, huh?
It returns an array of pairs (which are arrays as well) of all elements (keys and values) that your hashmap contains.
If you wish to return one element of a hash h, you will need to specify the key to identify the element. As the value for key k is h[k], the key-value pair, expressed as an array, is [k, h[k]]. If you wish to make that a hash with a single element, use Hash[[[k, h[k]]]].
For example, if
h = { "cat"=>"Sam", "dog"=>"Diva" }
and you only wanted to the element with key "cat", that would be
["cat", h["cat"]] #=> ["cat", "Sam"]
or
Hash[[["cat", h["cat"]]]] #=> {"cat"=>"Sam"}
With Ruby 2.1 you could alternatively get the hash like this:
[["cat", h["cat"]]].to_h #=> {"cat"=>"Sam"}
Let's look at a little more interesting case. Suppose you have an array arr containing some or all of the keys of a hash h. Then you can get all the key-value pairs for those keys by using the methods Enumerable#zip and Hash#values_at:
arr.zip(arr.values_at(*arr))
Suppose, for example,
h = { "cat"=>"Sam", "dog"=>"Diva", "pig"=>"Petunia", "owl"=>"Einstein" }
and
arr = ["dog", "owl"]
Then:
arr.zip(h.values_at(*arr))
#=> [["dog", "Diva"], ["owl", "Einstein"]]
In steps:
a = h.values_at(*arr)
#=> h.values_at(*["dog", "owl"])
#=> h.values_at("dog", "owl")
#=> ["Diva", "Einstein"]
arr.zip(a)
#=> [["dog", "Diva"], ["owl", "Einstein"]]
To instead express as a hash:
Hash[arr.zip(h.values_at(*arr))]
#=> {"dog"=>"Diva", "owl"=>"Einstein"}
You can get the key and value in one go - resulting in an array:
#h = {"cat"=>"Sam", "dog"=>"Phil"}
key, value = p h.assoc("cat") # => ["cat", "Sam"]
Use rassoc to search by value ( .rassoc("Sam") )

Iterate hash for specific range

How to pass range in hash to iterate from index 1 to last?
h = {}
h[1..-1].each_pair do |key,value|
puts "#{key} = #{value}
end
This code returning error. how may i pass range in hash ??
EDIT:
I want to print first key and value without any calculations.
From second key and value i want to do some calculation on my hash.
For that i have written this code ...
store_content_in_hash containing key and values.
first_key_value = store_content_in_hash.shift
f.puts first_key_value[1]
f.puts
store_content_in_hash.each_pair do |key,value|
store_content_in_hash[key].sort.each {|v| f.puts v }
f.puts
end
Any better way to solve out this problem ??
In Ruby 1.9 only:
Given a hash:
h = { :a => :b, :c => :d, :e => :f }
Go Like this:
Hash[Array(h)[1..-1]].each_pair do |key, value|
# ...
end
This will iterate through the following hash { :c => :d, :e => f } as the first key/value pair is excluded by the range.
Hashes have no concept of order. There is no such thing as the first or second element in a hash.
So you can't do what you want with hashes.
Hash is not about the ranges. It's about key value pairs. In ruby 1.8 hash is unordered hence you can't be sure in which order the keys and values will be iterated over which makes "range" thing obsolete. And I believe that you're doing something wrong (tm) in this situation. Can you elaborate on your problem?
On the other note you're getting an error because square brackets in Hash instance accepts keys. So if your hash does not contain 1..-1 as a key - you will get nil value and nil does not respond to each_pair. Try this to get a hold on this:
h = {(1..-1) => {:foo => :bar}}
h[1..-1].each_pair do |key,value|
puts "#{key} = #{value}"
end
As others have pointed out, Hashes are not about order. It's true that 1.9 hashes are ordered, but that's just a convenience, not their primary feature.
If the order is important to you, just use arrays instead of hashes. In your case, an array of pairs (two-element arrays) seems to fit the purpose. And if you need to access it by key, you can always easily convert it to a hash using Hash#[] method.

Resources