Specifying default value for Hash of hashes in Ruby [duplicate] - ruby

This question already has answers here:
Strange, unexpected behavior (disappearing/changing values) when using Hash default value, e.g. Hash.new([])
(4 answers)
Closed 5 years ago.
Lets say I have a Hash called person whose key is name and value is a hash having keys 'age' and 'hobby'. An entry in the hash person would look like
=> person["some_guy"] = {:hobby => "biking", :age => 30}
How would I go about specifying the default for the hash 'person'? I tried the following
=> person.default = {:hobby => "none", :age => 20}
but it doesn't work.
EDIT:
I was setting one attribute and expecting others to be auto-populated. For eg.
=> person["dude"][:age] += 5
and this is what I was seeing
=> person["dude"]
=> {:hobby => "none", :age => 25}
which is fine. However, typing person at the prompt, I get an empty hash.
=> person
=> {}
However, what I was expecting was
=> person
=> {"dude" => {:hobby => "none", :age => 25}}

Why It May Not Work for You
You can't call Hash#default if an object doesn't have the method. You need to make sure that person.is_a? Hash before it will work. Are you sure you don't have an array instead?
It's also worth noting that a hash default doesn't populate anything; it's just the value that's returned when a key is missing. If you create a key, then you still need to populate the value.
Why It Works for Me
# Pass a default to the constructor method.
person = Hash.new({hobby: "", age: 0})
person[:foo]
=> {:hobby=>"", :age=>0}
# Assign a hash literal, and then call the #default method on it.
person = {}
person.default = {hobby: "", age: 0}
person[:foo]
=> {:hobby=>"", :age=>0}
What You Probably Want
Since hash defaults only apply to missing keys, not missing values, you need to take a different approach if you want to populate a hash of hashes. One approach is to pass a block to the hash constructor. For example:
person = Hash.new {|hash,key| hash[key]={hobby: nil, age:0}}
=> {}
person[:foo]
=> {:hobby=>nil, :age=>0}
person[:bar]
=> {:hobby=>nil, :age=>0}
person
=> {:foo=>{:hobby=>nil, :age=>0}, :bar=>{:hobby=>nil, :age=>0}}

You can specify a hash's default value on instantiation by passing the default value to the .new method:
person = Hash.new({ :hobby => '', :age => 0 })

You can use Hash#default_proc to initialise a hash with default values dynamically.
h = Hash.new
h.default_proc = proc do |hash, key|
hash[key] = Hash.new(0) unless hash.include? key
end
h[0][0] # => 0
h[1][0] # => 0
h[0][0] = 1
h[0][0] # => 1
h[1][0] # => 0

Related

Define a hash with keys of class string without using hash rockets?

A hash (with key of class String) can be defined using hash rocket syntax like so:
h = {:name => 'Charles', "name" => 'John'}
Is there a way to define it using another notation?
I tried:
a = {name: "Charles", "name": "John"}
(irb):14: warning: key :name is duplicated and overwritten on line 14
a
# => {:name=>"John"}
Also note that without any overriding, I still can't spot a way to get the key to be class String:
b = {"name": "John"}
b
# => {:name=>"John"} # "name" (String) is stored as :name (Symbol)
AFAIK it's not possible to define a hash with any String keys using the more modern hash notation (the notation that doesn't use the hash rockets). Is there any way, or should the => has rockets be used when defining a hash with a key of class String?
The documentation really makes this pretty clear:
The older syntax for Hash data uses the “hash rocket,” =>:
h = {:foo => 0, :bar => 1, :baz => 2} h # => {:foo=>0, :bar=>1,
> :baz=>2}
Alternatively, but only for a Hash key that's a Symbol, you can use a
newer JSON-style syntax...

Add key-value for Ruby Hash from within the Hash

What's the best way to add a key-value pair to an hash object, from within the hash object itself?
The common way I know to add a new key to a hash is as follows:
hash = Hash.new
hash[:key] = 'value'
hash[:key] # => 'value'
What if I wan't to create a new hash which already has this key after its creation?
hash = Hash.new
hash[:key] # => 'value'
Is this possible? Thanks!
To create a Hash with an already initialized set of values you can do:
hash = { :key => 'value' }
hash[:key] # ===> This evaluates to 'value'
Just remember, the idiomatic way to create an empty hash in Ruby is:
hash = {}
Not hash = Hash.new like you exemplified.
Do you mean set the default value? is so, you could do with:
hash = Hash.new('value')
hash[:key] # => 'value'
Not sure what you mean i the other answers aren't what you want, you can create a hash with some keys and values allready filled like this
hash = {:key => 'value'} #{:key=>"value"}
and like the others said, the default value for key's not allready present is given by passing a block tot the hash at creation time like
hash = Hash.new('value') #{}
hash[:test] #"value"
or
h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" } #{}
h[:test] #"Go Fish: test"
Thje last sample teken from http://www.ruby-doc.org/core-1.9.3/Hash.html

dynamically finding value from key string in nested hash

I have a user inputed string called x_value whose value contains something like ticker|high. Whenever there is a |, that indicates that the latter is a child of the former. The purpose of the method is to return a specific value within a hash.
sections = []
object.x_value.split('|').each do |part|
sections << part.to_sym
end
I then want to drill down the data hash and retrieve the value of the last key.
data = {"ticker":{"high":529.5,"low":465,"avg":497.25,"vol":7520812.018}}
In this example
data[sections[0]][sections[1]] returns the expected 529.5 value. However, the user may have different hashes and different levels deep of nested key/values. How can I write this?
I have tried data[sections], but that didn't work.
Use Enumerable#reduce
data = {"ticker" => {"high" => 529.5, "low" => 465,"avg" => 497.25,"vol" => 7520812.018}}
"ticker|high".split('|').reduce(data) { |dat,val| dat[val] } #=> 592.5
more example:
data = {"more_ticker" => {"ticker" => {"high" => 529.5, "low" => 465,"avg" => 497.25,"vol" => 7520812.018}}}
"more_ticker|ticker|avg".split('|').reduce(data) { |dat,val| dat[val] }
#=> 497.25
You could also use recursion:
def getit(hash, x_value)
recurse(hash, x_value.split('|'))
end
def recurse(hash, keys)
k = keys.shift
keys.empty? ? hash[k] : recurse(hash[k], keys)
end
data = {"ticker" => {"high" => 529.5, "low" => 465}}
getit(data, "ticker|high") #=> 529.5
getit(data, "ticker") #=> {"high"=>529.5, "low"=>465}
data = {"more_ticker" => {"ticker" => {"high" => 529.5, "low" => 465}}}
getit(data, "more_ticker|ticker|low") #=> 465
getit(data, "more_ticker|ticker|avg") #=> nil

How to get the first key and value pair from a hash table in Ruby

I'm trying to get the first key and value key from a hash table in ruby. I don't know the key values of the hash because it is passed to the method. I cant find anywhere online how to find the first key/value as a separate hash table.
I think hash[0] will just try to find an element with a name 0 it just returns nil when I run the code.
I know I can find the key name and the value and then create a new hash from them but i wonder if there is an easier way to do this so I get a hash right away.
here is my code:
def rps_game_winner(game)
rock_in_hash = game.invert['R']
paper_in_hash = game.invert['P']
scissors_in_hash = game.invert['S']
if(rock_in_hash)
if(paper_in_hash)
return paper_in_hash;
elsif(scissors_in_hash)
return rock_in_hash
end
elsif(paper_in_hash)
if(rock_in_hash)
return paper_in_hash
elsif(scissors_in_hash)
return scissors_in_hash
end
end
key = game.keys[-1]
value = game.values[-1]
winner = {key => value}
return winner
end
game_one = { "Bob" => 'P', "Jim" => 'P' }
puts rps_game_winner(game_one)
This gets me the correct result the problem is I don't understand why it's -1 instead of zero...
And i was hoping there was a better way to get the first key/value pair of a hash table instead of creating new hash table with the key and value you retrieved from the previous table.
You can just do
key, value = hash.first
or if you prefer:
key = hash.keys[0]
value = hash.values[0]
Then maybe:
new_hash = {key => value}
There is a shorter answer that does not require you to use extra variables:
h = { "a" => 100, "b" => 200 , "c" => 300, "d" => 400, "e" => 500}
Hash[*h.first] #=> {"a" => 100}
Or if you want to retrieve a key/value at a any single position
Hash[*h.to_a.at(1)] #=> {"b" => 200}
Or retrieve a key/values from a range of positions:
Hash[h.to_a[1,3]] #=> {"b"=>200, "c"=>300, "d"=>400}
[hash.first].to_h
Another way to do it.
my_hash = { "a" => "first", "b" => "second" }
{ my_hash.keys.first => my_hash.values.first }
This works too

Iterating over Ruby hash while comparing values to another Ruby hash

I have 2 Ruby objects that I am converting to hashes: one from XML and another from JSON. When I puts the variable name I get hash, so it appears that I'm doing that correctly.
The format is several records in the format below.
Format of hash one (smithjj being a unique username):
{ smithjj => {office => 331, buidling => 1} }
Format of hash 2:
{"Data"=>{"xmlns:dmd"=>"http://www.xyz.com/schema/data-metadata",
"dmd:date"=>"2012-03-06", "Record"=>{"PCI"=>{"DPHONE3"=>nil, "OPHONE3"=>"111",
"DTY_DOB"=>"1956", "TEACHING_INTERESTS"=>nil, "FAX1"=>"123", "ROOMNUM"=>"111",
"DTD_DOB"=>"5", "DTM_DOB"=>"11", "WEBSITE"=>"www.test.edu", "FAX2"=>"324",
"ENDPOS"=>"Director", "LNAME"=>"Smith", "FAX3"=>"4891", "MNAME"=>"Thomas",
"GENDER"=>"Male", "ALT_NAME"=>nil, "PFNAME"=>"TG", "id"=>"14101823488",
"RESEARCH_INTERESTS"=>nil, "BIO"=>"", "CITIZEN"=>"Yes", "EMAIL"=>"test#email",
"SUFFIX"=>nil, "DPHONE1"=>nil}, "termId"=>"234", "IndexEntry"=>{"text"=>"Other",
"indexKey"=>"DEPARTMENT", "entryKey"=>"Other"}, "dmd:surveyId"=>"23424",
"username"=>"smithers", "userId"=>"23324"}, "xmlns"=>"http://www.adsfda.com/"}}
I want to iterate over each unique username in the first hash and compare values from the PCI section of the second hash to the values in the first hash. The keys are different names so I planned on pairing them up.
I've tried several ways of doing it, but I keep getting a string integer error, so I must not be iterating correctly. I'm doing an .each do block, but all the examples I see show a simple hash, not a key => key => value, key => value.
Any direction is much appreciated.
Here's how you should be able to do the iteration properly for hashes, if you're not already using this:
h = {:foo => 42, :bar => 43, 44 => :baz}
h.each {|key, val| puts "The value at #{key} is #{val}."}
This produces:
The value at foo is 42.
The value at bar is 43.
The value at 44 is baz.
As for the last part of your question, could you please make it a little clearer? e.g., what error are you getting exactly? This could help us understand the issue more.
EDIT: If what you'd like to do is compare certain values against others, try something like this:
h1 = {:foo => 42, :bar => 43, 44 => :baz, :not_used => nil}
h2 = {:life_universe => 42, :fourty_three => 45, :another_num => 44, :unused => :pancakes}
# Very easy to add more
comparisons = {:foo => :life_universe, :bar => :fourty_three, :baz => :another_num}
def all_match_up?(hash1, hash2, comps)
comparisons.each {|key, val|
if key != val
# They don't match!
puts "#{key} doesnt match #{val}!"
return false
end
}
# They all match!
true
end
all_match_up?(h1, h2, comparisons)
Try this to see what happens ;)

Resources