Changing a Hash in ruby using an enumerator - ruby

Here is my example program:
what = {:banana=>:fruit, :pear=>:fruit, :sandal=>:fruit, :panda=>:fruit, :apple=>:fruit}
what.map do |w|
p "is this right?"
awesome_print w
fix = gets
fix.chop!
if (fix == "N")
p "Tell me what it should be"
correction = gets
w[1] = correction.chop!.to_sym
end
p w
end
I run it, and I get this (my input included):
"is this right?"
[
[0] :banana,
[1] :fruit
]
Y
[:banana, :fruit]
"is this right?"
[
[0] :pear,
[1] :fruit
]
Y
[:pear, :fruit]
"is this right?"
[
[0] :sandal,
[1] :fruit
]
N
"Tell me what it should be"
footwear
[:sandal, :footwear]
"is this right?"
[
[0] :panda,
[1] :fruit
]
N
"Tell me what it should be"
animal
[:panda, :animal]
"is this right?"
[
[0] :apple,
[1] :fruit
]
Y
[:apple, :fruit]
=> [[:banana, :fruit], [:pear, :fruit], [:sandal, :footwear], [:panda, :animal], [:apple, :fruit]]
>> what
=> {:banana=>:fruit, :pear=>:fruit, :sandal=>:fruit, :panda=>:fruit, :apple=>:fruit}
My question is how can I change the Hash? irb tells me when I run the program that each enumerated element is processed, but the results aren't saved in my hash what.

If you want to mutate the hash in place (as you seem to want), simply do this:
my_hash.each do |key,value| # map would work just as well, but not needed
my_hash[key] = some_new_value
end
If you want to create a new hash, without changing the original:
new_hash = Hash[ my_hash.map do |key,value|
[ key, new_value ]
end ]
The way this works is that Enumerable#map returns an array (in this case an array of two-element key/value pairs), and Hash.[] can turn [ [a,b], [c,d] ] into { a=>b, c=>d }.
What you were doing—hash.map{ … }—was mapping each key/value pair to a new value and creating an array…and then doing nothing with that array. While there is Array#map! which will destructively mutate an array in place, there is no equivalent Hash#map! to destructively mutate a hash in a single step.
Note also that if you want to destructively mutate a Hash—or any other object that references other mutable objects—in place you can just destructively mutate those objects during normal iteration:
# A simple hash with mutable strings as values (not symbols)
h = { a:"zeroth", b:"first", c:"second", d:"third" }
# Mutate each string value
h.each.with_index{ |(char,str),index| str[0..-3] = index.to_s }
p h #=> {:a=>"0th", :b=>"1st", :c=>"2nd", :d=>"3rd"}
However, since you are using symbols for the values in your sample code—and since symbols are not mutable—this final note does not directly apply there.

Instead of:
w[1] = correction.chop!.to_sym
Try assigning to the hash directly:
what[w[0]] = correction.chop!.to_sym
Ruby is creating that w array just to pass you the key and value. Assigning to that array isn't going to change your hash; it's only changing that temporary array.

Related

Merging Three hashes and getting this resultant hash

I have read the xls and have formed these three hashes
hash1=[{'name'=>'Firstname',
'Locator'=>'id=xxx',
'Action'=>'TypeAndWait'},
{'name'=>'Password',
'Locator'=>'id=yyy',
'Action'=>'TypeAndTab'}]
Second Hash
hash2=[{'Test Name'=>'Example',
'TestNumber'=>'Test1'},
{'Test Name'=>'Example',
'TestNumber'=>'Test2'}]
My Thrid Hash
hash3=[{'name'=>'Firstname',
'Test1'=>'four',
'Test2'=>'Five',
'Test3'=>'Six'},
{'name'=>'Password',
'Test1'=>'Vicky',
'Test2'=>'Sujin',
'Test3'=>'Sivaram'}]
Now my resultant hash is
result={"Example"=>
{"Test1"=>
{'Firstname'=>
["id=xxx","four", "TypeAndWait"],
'Password'=>
["id=yyy","Vicky", "TypeAndTab"]},
"Test2"=>
{'Firstname'=>
["id=xxx","Five", "TypeAndWait"],
'Password'=>
["id=yyy","Sujin", "TypeAndTab"]}}}
I have gotten this result, but I had to write 60 lines of code in my program, but I don't think I have to write such a long program when I use Ruby, I strongly believe some easy way to achieve this. Can some one help me?
The second hash determines the which testcase has to be read, for an example, test3 is not present in the second testcase so resultant hash doesn't have test3.
We are given three arrays, which I've renamed arr1, arr2 and arr3. (hash1, hash2 and hash3 are not especially good names for arrays. :-))
arr1 = [{'name'=>'Firstname', 'Locator'=>'id=xxx', 'Action'=>'TypeAndWait'},
{'name'=>'Password', 'Locator'=>'id=yyy', 'Action'=>'TypeAndTab'}]
arr2 = [{'Test Name'=>'Example', 'TestNumber'=>'Test1'},
{'Test Name'=>'Example', 'TestNumber'=>'Test2'}]
arr3=[{'name'=>'Firstname', 'Test1'=>'four', 'Test2'=>'Five', 'Test3'=>'Six'},
{'name'=>'Password', 'Test1'=>'Vicky', 'Test2'=>'Sujin', 'Test3'=>'Sivaram'}]
The drivers are the values "Test1" and "Test2" in the hashes that are elements of arr2. Nothing else in that array is needed, so let's extract those values (of which there could be any number, but here there are just two).
a2 = arr2.map { |h| h['TestNumber'] }
#=> ["Test1", "Test2"]
Next we need to rearrange the information in arr3 by creating a hash whose keys are the elements of a2.
h3 = a2.each_with_object({}) { |test,h|
h[test] = arr3.each_with_object({}) { |f,g| g[f['name']] = f[test] } }
#=> {"Test1"=>{"Firstname"=>"four", "Password"=>"Vicky"},
# "Test2"=>{"Firstname"=>"Five", "Password"=>"Sujin"}}
Next we need to rearrange the content of arr1 by creating a hash whose keys match the keys of values of h3.
h1 = arr1.each_with_object({}) { |g,h| h[g['name']] = g.reject { |k,_| k == 'name' } }
#=> {"Firstname"=>{"Locator"=>"id=xxx", "Action"=>"TypeAndWait"},
# "Password"=>{"Locator"=>"id=yyy", "Action"=>"TypeAndTab"}}
It is now a simple matter of extracting information from these three objects.
{ 'Example'=>
a2.each_with_object({}) do |test,h|
h[test] = h3[test].each_with_object({}) do |(k,v),g|
f = h1[k]
g[k] = [f['Locator'], v, f['Action']]
end
end
}
#=> {"Example"=>
# {"Test1"=>{"Firstname"=>["id=xxx", "four", "TypeAndWait"],
# "Password"=>["id=yyy", "Vicky", "TypeAndTab"]},
# "Test2"=>{"Firstname"=>["id=xxx", "Five", "TypeAndWait"],
# "Password"=>["id=yyy", "Sujin", "TypeAndTab"]}}}
What do you call hash{1-2-3} are arrays in the first place. Also, I am pretty sure you have mistyped hash1#Locator and/or hash3#name. The code below works for this exact data, but it should not be hard to update it to reflect any changes.
hash2.
map(&:values).
group_by(&:shift).
map do |k, v|
[k, v.flatten.map do |k, v|
[k, hash3.map do |h3|
# lookup a hash from hash1
h1 = hash1.find do |h1|
h3['name'].start_with?(h1['Locator'])
end
# can it be nil btw?
[
h1['name'],
[
h3['name'][/.*(?=-id)/],
h3[k],
h1['Action']
]
]
end.to_h]
end.to_h]
end.to_h

Does Ruby Hash's keep a separate list of read values vs assigned values? [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 4 years ago.
This is related to Ruby hash default value behavior
But maybe the explanation there doesn't include this part: it seems that Ruby's Hash default value are separate whether you "read it", or see "what is set"?
One example is:
foo = Hash.new([])
foo[123].push("hi")
p foo # => {}
p foo[123] # => ["hi"]
p foo # => {}
How is it that foo[123] has a value, but foo is all empty, is somewhat beyond my comprehension... the only way I can understand it is that Ruby Hash keeps a separate list for the "read" or "getter", while somehow the "internal" assigned value are different.
If one of Ruby's design principles is "to have the least amount of surprise to the programmers", then the foo is empty but foo[123] is something, is somewhat in this case, a surprise to me.
(I haven't seen that in other languages actually... if there is a case where another language has similar behavior, maybe it is easier to make a connection.)
Suppose `
h = Hash.new(:cat)
h[:a] = 1
h[:b] = 2
h #=> {:a=>1, :b=>2}
Now
h[:a] #=> 1
h[:b] #=> 2
h[:c] #=> :cat
h[:d] #=> :cat
h #=> {:a=>1, :b=>2}
h = Hash.new(:cat) defines an empty hash h with a default value of :cat. This means that if h does not have a key k, h[k] will return :cat, nothing more, nothing less. As you can see above, executing h[k] does not change the hash when k is :c or :d.
On the other hand,
h[:c] = h[:c]
#=> :c
h #=> {:a=>1, :b=>2, :c=>:cat}
Confused? Let me write this without the syntactic sugar:
h.[]=(:d, h.[](:d))
#=> :cat
h #=> {:a=>1, :b=>2, :d=>:cat}
The default value is returned by h.[](:d) (i.e., h[:d]) whereas Hash#[]= is an assignment method (that takes two arguments, a key and a value) to which the default does not apply.
A common use of this default is to create a counting hash:
a = [1,3,1,4,2,5,4,4]
h = Hash.new(0)
a.each { |x| h[x] = h[x] + 1 }
h #=> {1=>2, 3=>1, 4=>3, 2=>1, 5=>1}
Initially, when h is empty and x #=> 1, h[1] = h[1] + 1 will evaluate to h[1] = 0 + 1, because (since h has no key 1) h[1] on the right side of the equality is set equal to the default value of zero. The next time 1 is passed to the block (x #=> 1), x[1] = x[1] + 1, which equals x[1] = 1 + 1. This time the default value is not used because h now has a key 1.
This would normally be written (incidentally):
a.each_with_object(Hash.new(0)) { |x,h| h[x] += 1 }
#=> {1=>2, 3=>1, 4=>3, 2=>1, 5=>1}
One generally does not want the default value to be a collection, such as an array or hash. Consider the following:
h = Hash.new([])
[1,2,3].map { |n| h[n] = h[n] }
h #=> {1=>[], 2=>[], 3=>[]}
Now observe:
h[1] << 2
h #=> {1=>[2], 2=>[2], 3=>[2]}
This is normally not the desired behaviour. It has happened because
h.map { |k,v| v.object_id }
#=> [25886508, 25886508, 25886508]
That is, all the values are the same object, so if the value of one key is changed the values of all other keys are changed as well.
The way around this is to use a block when defining the hash:
h = Hash.new { |h,k| h[k]=[] }
[1,2,3].each { |n| h[n] = h[n] }
h #=> {1=>[], 2=>[], 3=>[]}
h[1] << 2
h #=> {1=>[2], 2=>[], 3=>[]}
h.map { |k,v| v.object_id }
#=> [24172884, 24172872, 24172848]
When the hash h does not have a key k the block { |h,k| h[k]=[] } is executed and returns an empty array specific to that key.
The statement:
foo = Hash.new([])
creates a new Hash that has an empty array ([] as default value). The default value is the value returned by Hash::[] when its argument is not a key present in the hash.
The statement:
foo[123]
invokes Hash::[] and, because the hash is empty (the key 123 is not present in the hash), it returns a reference to the default value which is an object of type Array.
The statement above doesn't create the 123 key in the hash.
Ruby objects are always passed and returned by reference. This means that the statement above doesn't return a copy of the default value of the hash but a reference to it.
The statement:
foo[123].push("hi")
modifies the above mentioned array. Now, the default value of the foo hash is not an empty array any more; it is the array ["hi"]. But the has is still empty; none of the above statements added some (key, value) pair to it.
How is it that foo[123] has a value
foo[123] doesn't have any value, the key 123 is not present in the hash (the hash is empty). A subsequent call to foo[123] returns a reference to the default value again and the default value now it's ["hi"]. And a call to foo[456] or foo['abc'] also returns a reference to the same default value.
You didn't actually change the value of key 123, you're just accessing the default value [] you provided during initialization. You can confirm this if you inspect a different value like foo[0].
If you would do this:
foo[123] = ["hi"]
you could see the new entry, because you've created a new array under the key 123.
Edit
When you call foo[123].push("hi"), you're mutating the (default) value instead of adding a new entry.
Calling foo[123] += ["hi"] creates a new array under the given key, replacing the previous one if it existed, which will show the behavior you desire.
Printing out the hash with:
p foo
only prints the values stored in the hash. It does not display the default value (or anything added to the default array).
When you execute:
p foo[123]
Because 123 does not exist, it access the default value.
If you added two values to the default value:
foo[123].push("hi")
foo[456].push("hello")
your output would be:
p foo # => {}
p foo[123] # => ["hi","hello"]
p foo # => {}
Here, poo[123] does again still not exist, so it prints out the contents of the default value.

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") )

Modifying an existing hash value by x and returning the hash

I'm trying to increment all values of a hash by a given amount and return the hash. I am expecting:
add_to_value({"a" => 1, "c" => 2,"b"=> 3}, 1)
# => {"a" => 2, "c" => 3,"b"=> 4}
I'm thinking:
def add_to_value(hash, x)
hash.each {|key,value| value + x}
end
This returns:
{"a"=>1, "b"=>3, "c"=>2}
Why is hash sorted alphabetically?
You're super close, without any extra gems needed:
def add_to_value(hash, x)
hash.each {|key,value| hash[key] += x }
end
Just iterate the hash and update each value-by-key. #each returns the object being iterated on, so the result will be the original hash, which has been modified in place.
If you want a copy of the original hash, you can do that pretty easily, too:
def add_to_value(hash, x)
hash.each.with_object({}) {|(key, value), out| out[key] = value + x }
end
That'll define a new empty hash, pass it to the block, where it collects the new values. The new hash is returned from #with_object, and is thus returned out of add_to_value.
You can do the following to increment values:
hash = {}
{"a" => 1, "c" => 2,"b"=> 3}.each {|k,v| hash[k]=v+1}
hash
=>{"a"=>2, "c"=>3, "b"=>4}
And the hash will be sorted as you want.
The problem becomes trivial if you use certain gems, such as y_support. Type in your command line gem install y_support, and enjoy the extended hash iterators:
require 'y_support/core_ext/hash'
h = { "a"=>1, "c"=>3, "b"=>2 }
h.with_values do |v| v + 1 end
#=> {"a"=>2, "c"=>4, "b"=>3}
As for your sorting problem, I was unable to reproduce it.
Of course, a less elegant solution is possible without installing a gem:
h.each_with_object Hash.new do |(k, v), h| h[k] = v + 1 end
The gem also gives you Hash#with_keys (which modifies keys) and Hash#modify (which modifies both keys and values, kind of mapping from hash to hash), and banged versions Hash#with_values!, #with_keys! that modify the hash in place.

Search hash values for substring and return key in Ruby

I have an array:
array = ["One is enough", "Two is a couple", "Where's the beef?", "One"]
I then have a hash:
hash = {"one" => "Defined",
"two" => "Test"
}
I need to loop over each object in array and see if that object contains a substring that is found in the hash keys. If it is found, it should return the hash value. Otherwise, it should return undefined. The end goal is to create an array that looks like this:
final_array = ["Defined,"Test","Undefined","Defined"]
How can I write that loop effectively?
Here is an approach :
array = ["One is enough", "Two is a couple", "Where's the beef?", "One"]
hash = {"one" => "Defined","two" => "Test"}
array.map{|e| hash.find(proc{["undefined"]}){|k,v| e.downcase.include? k}.last}
# => ["Defined", "Test", "undefined", "Defined"]
Explanation:
Enumerable#find will be good idea. As doc is saying - Passes each entry in enum to block. Returns the first for which block is not false. If no object matches, calls ifnone and returns its result when it is specified, or returns nil otherwise.
I used Array#map,and passing each element string,to the block. Now inside the block I called #find on hash. Now hash.find passing each key/value pair to the hash.find method block. Inside that block I am calling String#include? method on e,passing key as argument to the #include? method. If #include? test results true,then key/value of that iteration is returned,otherwise default argument proc{["undefined"]}.call is being performed.
Hope that helps!
hash = {"one" => "Defined", "two" => "Test"}
array = ["One is enough", "Two is a couple", "Where's the beef?", "One"]
hash.default = "Undefined"
keys = hash.keys
array.map {|a| hash[(a.split.map(&:downcase) & keys).first]}
if hash does not contain a key 'k', hash[k] => "Undefined"
a.split.map(&:downcase) changes, for example, a = "One is Enough" to "one is enough"
the intersection with keys equals ["one"], ["two"], [nil], ["one"], respectively
.first is to extract the hash key from the 1-element arrays
the hash is evaluated at the computed hash key, with hash[nil] => "Undefined" (default)

Resources