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

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...

Related

How to access a symbol hash key using a variable in Ruby

I have an array of hashes to write a generic checker for, so I want to pass in the name of a key to be checked. The hash was defined with keys with symbols (colon prefixes). I can't figure out how to use the variable as a key properly. Even though the key exists in the hash, using the variable to access it results in nil.
In IRB I do this:
>> family = { 'husband' => "Homer", 'wife' => "Marge" }
=> {"husband"=>"Homer", "wife"=>"Marge"}
>> somevar = "husband"
=> "husband"
>> family[somevar]
=> "Homer"
>> another_family = { :husband => "Fred", :wife => "Wilma" }
=> {:husband=>"Fred", :wife=>"Wilma"}
>> another_family[somevar]
=> nil
>>
How do I access the hash key through a variable? Perhaps another way to ask is, how do I coerce the variable to a symbol?
You want to convert your string to a symbol first:
another_family[somevar.to_sym]
If you want to not have to worry about if your hash is symbol or string, simply convert it to symbolized keys
see: How do I convert a Ruby hash so that all of its keys are symbols?
You can use the Active Support gem to get access to the with_indifferent_access method:
require 'active_support/core_ext/hash/indifferent_access'
> hash = { somekey: 'somevalue' }.with_indifferent_access
=> {"somekey"=>"somevalue"}
> hash[:somekey]
=> "somevalue"
> hash['somekey']
=> "somevalue"
Since your keys are symbols, use symbols as keys.
> hash = { :husband => 'Homer', :wife => 'Marge' }
=> {:husband=>"Homer", :wife=>"Marge"}
> key_variable = :husband
=> :husband
> hash[key_variable]
=> "Homer"
If you use Rails with ActiveSupport, then do use HashWithIndifferentAccess for flexibility in accessing hash with either string or symbol.
family = HashWithIndifferentAccess.new({
'husband' => "Homer",
'wife' => "Marge"
})
somevar = "husband"
puts family[somevar]
#Homer
somevar = :husband
puts family[somevar]
#Homer
The things that you see as a variable-key in the hash are called Symbol is a structure in Ruby. They're primarily used either as hash keys or for referencing method names. They're immutable, and Only one copy of any symbol exists at a given time, so they save memory.
You can convert a string or symbol with .to_sym or a symbol to string with .to_s to illustrate this let me show this example:
strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]
symbolArray = [:HTML, :CSS, :JavaScript, :Python, :Ruby]
# Add your code below!
symbols = Array.new
strings.each {|x|
symbols.push(x.to_sym)
}
string = Array.new
symbolArray .each {|x|
string.push(x.to_s)
}
print symbols
print string
the result would be:
[:HTML, :CSS, :JavaScript, :Python, :Ruby]
["HTML", "CSS", "JavaScript", "Python", "Ruby"]
In ruby 9.1 you would see the symbols with the colons (:) in the right instead:
movies = { peter_pan: "magic dust", need_4_speed: "hey bro", back_to_the_future: "hey Doc!" }
I just wanted to make this point a litter more didactic so who ever is reading this can used.
One last thing, this is another way to solve your problem:
movie_ratings = {
:memento => 3,
:primer => 3.5,
:the_matrix => 3,
}
# Add your code below!
movie_ratings.each_key {|k|
puts k.to_s
}
result:
memento
primer
the_matrix

Specifying default value for Hash of hashes in Ruby [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 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

Reading and writing Sinatra params using symbols, e.g. params[:id]

My form receives data via POST. When I do puts params I can see:
{"id" => "123", "id2" => "456"}
now the commands:
puts params['id'] # => 123
puts params[:id] # => 123
params['id'] = '999'
puts params # => {"id" => "999", "id2" => "456"}
but when I do:
params[:id] = '888'
puts params
I get
{"id" => "999", "id2" => "456", :id => "888"}
In IRB it works fine:
params
# => {"id2"=>"2", "id"=>"1"}
params[:id]
# => nil
params['id']
# => "1"
Why can I read the value using :id, but not set the value using that?
Hashes in Ruby allow arbitrary objects to be used as keys. As strings (e.g. "id") and symbols (e.g. :id) are separate types of objects, a hash may have as a key both a string and symbol with the same visual contents without conflict:
irb(main):001:0> { :a=>1, "a"=>2 }
#=> {:a=>1, "a"=>2}
This is distinctly different from JavaScript, where the keys for objects are always strings.
Because web parameters (whether via GET or POST) are always strings, Sinatra has a 'convenience' that allows you to ask for a parameter using a symbol and it will convert it to a string before looking for the associated value. It does this by using a custom default_proc that calls to_s when looking for a value that does not exist.
Here's the current implementation:
def indifferent_hash
Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
end
However, it does not provide a custom implementation for the []=(key, val) method, and thus you can set a symbol instead of the string.

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

Ruby - getting value of hash

I have a hash like
{:key1 => "value1", :key2 => "value2"}
And I have a variable k which will have the value as 'key1' or 'key2'.
I want to get the value of k into a variable v.
Is there any way to achieve this with out using if or case? A single line solution is preferred. Please help.
Convert the key from a string to a symbol, and do a lookup in the hash.
hash = {:key1 => "value1", :key2 => "value2"}
k = 'key1'
hash[k.to_sym] # or iow, hash[:key1], which will return "value1"
Rails uses this class called HashWithIndifferentAccess that proves to be very useful in such cases. I know that you've only tagged your question with Ruby, but you could steal the implementation of this class from Rails' source to avoid string to symbol and symbol to string conversions throughout your codebase. It makes the value accessible by using a symbol or a string as a key.
hash = HashWithIndifferentAccess.new({:key1 => "value1", :key2 => "value2"})
hash[:key1] # "value1"
hash['key1'] # "value1"

Resources