Ruby: what's the difference using property with colons and quotes - ruby

I'm a newbie on Ruby, just doing a fast upgrade in a existent project and I'm wondering what's the difference between object record and to_validate.
puts to_validated.class
# Hash
puts to_validated
# {"data"=>{"name"=>"david"}, "metadata"=>{"body_size"=>"16", "collector_ip"=>"172.22.0.1", "collector_timestamp"=>1579608324863, "event"=>"whatever", "version"=>"1.0"}}
puts record.class
# Hash
puts record
# {:data=>{"name"=>"david"}, :metadata=>{"body_size"=>"16", "collector_ip"=>"172.22.0.1", "collector_timestamp"=>1579610268940, "event"=>"default", "version"=>"1.0.0"}}
The only difference on those objects is colons on data and metadata. Is possible to convert colons into quotes?
I know it's a dumb question but I'm applying a fix in this project and using a third party library that is failing using record object.

You can use Hash.transform_keys which was introduced to Ruby in version 2.5 to change symbolized keys into strings:
record.transform_keys { |k| k.to_s }

In Ruby hashes are a key value data structure. The keys can actually be a mix of any kind of objects:
hash_with_numerical_keys = {
1 => 'A',
2 => 'B',
3 => 'C'
3.5 => 'D'
}
hash_with_string_keys = {
'a' => 1,
'b' => 2,
'c' => 3
}
hash_with_symbols = {
:a => 1,
:b => 2,
:c => 3
}
# can also be declared as
hash_with_symbols = {
a: 1,
b: 2,
c: 3
}
hash_with_singletons = {
nil => 0,
true => 1,
false => 2
}
Symbols are commonly used as only one instance of a symbol ever exists. They are thus really effective to compare. String keys mostly come into play when you're dealing with JSON or some kind of external data.
The hash you are looking at contains a mixture of string and symbol keys:
{:data=>{"name"=>"david"}, :metadata=>{"body_size"=>"16", "collector_ip"=>"172.22.0.1", "collector_timestamp"=>1579610268940, "event"=>"default", "version"=>"1.0.0"}}
In Ruby 2.5 you can use hash#transform_keys to change the keys into strings:
hash.transform_keys(&:to_s)
In earlier versions you can do:
hash.each_with_object({}) do |(k,v), new_hash|
new_hash[k.to_s] = v
end
If you are using Rails or just ActiveSupport you can use Hash#stringify_keys, Hash#deep_stringify_keys or HashWithIndifferentAccess

Related

JSON with symbols and strings not readable

I have the following JSON:
{ :a => 1, "b" => "test" }
jsonObject[:b] does not give me any data, whereas for a JSON with all keys as strings,
{ "a" => 1, "b" => "test" }
it works fine:
jsonObject[:b] # => "test"
Is there a constraint against using a symbol and key in the same JSON object?
I suggest to parse a JSON to a Hash before using, like
require 'json'
JSON.parse("{...}")
and convert a hash to a JSON string by
hash.to_json
all keys of symbols and strings are converted into strings.
require 'json'
a = {:a => '12', 'b' => '23'}
p aa = a.to_json #=> "{\"a\":\"12\",\"b\":\"23\"}"
p JSON.parse(aa) #=> {"a"=>"12", "b"=>"23"}
It might be possible that you are sometimes dealing with a simple Hash and sometimes with a HashWithIndifferentAccess. The Rails' params for example allow indifferent access by default. This might explain your confusion:
hash = { :a => 1, 'b' => 2 }
hash[:a]
#=> 1
hash['b']
#=> 2
hash[:b]
#=> nil
But with a HashWithIndifferentAccess:
hash = hash.with_indifferent_access
hash[:a]
#=> 1
hash['b']
#=> 2
hash[:b]
#=> 2

what is difference between store vs merge in ruby hashes?

i create a hash:
a = {}
=> {}
then:
a.store(:b, {})
=> {}
and:
a.merge!(c: {})
=> {:b=>{}, :c=>{}}
what are differences actually?
store is an assignment method.
a = {}
# => {}
a.store(:b, {})
a
# => {:b=>{}}
# Here you are assigning a key :b with empty hash {}
Another example to make it clearer:
a = {}
# => {}
a.store("key", "value")
a
# => {"key"=>"value"}
merge on the other hand manipulates your existing hash by merging with a different hash.
Example:
a = {}
# => {}
a.merge({"key" => "value"})
# => {"key"=>"value"}
a
# => {} # original value still unchanged
a.merge!({"key" => "value"})
# => {"key"=>"value"}
a
# => {"key"=>"value"} # original value updated
However unless you use merge! a's value will not get changed i.e. merge will occur only for return.
what are differences actually?
I think the main difference is merge! will let you decide which value to keep when duplicate key is provided, since it expects a block as well.
On the other hand, when you use store, the previous value will be replaced by the latest value when duplicate key is provided.
store
h1 = { "a" => 100, "b" => 200 }
h1.store("b", 254)
#=> {"a"=>100, "b"=>254}
merge!
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h1.merge!(h2) { |key, v1, v2| v1 }
#=> {"a"=>100, "b"=>200, "c"=>300}
store takes just one key/value tuple as input and returns the stored value.
h1 = { foo: 'bar' }
h1.store(:baz, 1) #=> 1
h1 #=> { foo: 'bar', baz: 1 }
Whereas merge! accepts a hash as input and returns the updated hash:
h2 = { foo: 'bar' }
h2.merge!(baz: 1, buz: 2) #=> { foo: 'bar', baz: 1, buz: 2 }
h2 #=> { foo: 'bar', baz: 1, buz: 2 }
merge! takes one argument, which is hash to merge into original. store takes two arguments, which is key and value to store. Therefore, with merge!, you can add multiple keys to original hash, like this:
a = {}
a.merge!(a: 'a', b: 'b')
a
# => {:a => "a", :b => "b"}
For a hash h, Hash#store has the same effect as Hash#[]=: they both either add one key-value pair k=>v to h (if h does not have a key k) or modify the value of key k (if the hash already contains the key). Also, they both return v.
Hash#merge! (aka update) has two forms. The first does the same thing as store, except it does it for each key-value pair in another hash. The second form uses a block to determine the values of keys that are present in both hashes being merged. Please refer to the docs for details on that form of the method. Both forms of merge! return the "merged" hash.
Hash#merge is not a relevant comparison as it does not mutate the hash.

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

Is there a built-in Ruby method for "reshaping" a hash?

I have this hash which I retrieve from a database:
original_hash = {
:name => "Luka",
:school => {
:id => "123",
:name => "Ieperman"
},
:testScores => [0.8, 0.5, 0.4, 0.9]
}
I'm writing an API and want to return a slightly different hash to the client:
result = {
:name => "Luka",
:schoolName => "Ieperman",
:averageScore => 0.65
}
This doesn't work because the method reshape doesn't exist. Does it exist by another name though?
result = original_hash.reshape do |hash|
{
:name => hash[:name],
:school => hash[:school][:name],
:averageScore => hash[:testScores].reduce(:+).to_f / hash[:testScores].count
}
end
I'm new to Ruby so thought I'd ask before I go off overriding core classes. I'm sure it must exist as I always find myself reshaping hashes when writing an API. Or am I totally missing something?
The implementation is dead simple but, like I said, I don't want to override Hash if I don't need to:
class Hash
def reshape
yield(self)
end
end
BTW, I know about this:
result = {
:name => original_hash[:name],
:school => original_hash[:school][:name],
:averageScore => original_hash[:testScores].reduce(:+).to_f / original_hash[:testScores].count
}
But sometimes I don't have an original_hash variable and instead I'm operating straight off a return value, or I'm inside a one liner where this block based approach would be convenient.
Real World example:
#get the relevant user settings from the database, and reshape the hash into the form we want
settings = users.find_one({:_id => oid(a[:userID])}, {:emailNotifications => 1, :newsletter => 1, :defaultSocialNetwork => 1}).reshape do |hash|
{
:emailNotifications => hash[:emailNotifications] == 1,
:newsletter => hash[:newsletter] == 1,
:defaultSocialNetwork => hash[:defaultSocialNetwork]
}
end rescue fail
If you're using Ruby >= 1.9, try a combination of Object#tap and Hash#replace
def foo(); { foo: "bar" }; end
foo().tap { |h| h.replace({original_foo: h[:foo]}) }
# => { :original_foo => "bar" }
Since Hash#replace works in-place, you might find this a bit safer:
foo().clone.tap { |h| h.replace({original_foo: h[:foo]}) }
But this is getting a bit noisy. I'd probably go ahead and monkey-patch Hash at this stage.
From an API perspective, you may be looking for a representer object to sit between your internal model, and the API representation (prior to format-based serialisation). This doesn't work using the shortest, convenient Ruby syntax inline for a hash, but is a nice declarative approach.
For instance, the Grape gem (other API frameworks are available!) might solve the same real-world problem as:
# In a route
get :user_settings do
settings = users.find_one({:_id => oid(a[:userID])}, {:emailNotifications => 1, :newsletter => 1, :defaultSocialNetwork => 1})
present settings, :with => SettingsEntity
end
# Wherever you define your entities:
class SettingsEntity < Grape::Entity
expose( :emailNotifications ) { |hash,options| hash[:emailNotifications] == 1 }
expose( :newsletter ) { |hash,options| hash[:newsletter] == 1 }
expose( :defaultSocialNetwork ) { |hash,options| hash[:defaultSocialNetwork] }
end
This syntax is more geared towards handling ActiveRecord, or similar models, and not hashes though. So not a direct answer to your question, but I think implied by you building up an API. If you put in a representer layer of some kind now (not necessarily grape-entity), you will be thankful for it later, as you'll be better able to manage your model-to-API data mappings when they need to change.
You can replace the call to "reshape" with the builtin method Object#instance_eval and it will work exactly as such. Note however that there may be some unexpected behavior since you evaluating code in the context of the receiving object (e.g. if using "self").
result = original_hash.instance_eval do |hash|
# ...
This abstraction does not exist in the core but people uses it (with different names, pipe, into, as, peg, chain, ...). Note that this let-abstraction is useful not only for hashes, so add it to the class Object.
Is there a `pipe` equivalent in ruby?
if you put your hashes in a array you could use the map function to convert the entries
I can't think of anything that will do this magically, since you're essentially wanting to remap an arbitrary data structure.
Something you may be able to do is:
require 'pp'
original_hash = {
:name=>'abc',
:school => {
:name=>'school name'
},
:testScores => [1,2,3,4,5]
}
result = {}
original_hash.each {|k,v| v.is_a?(Hash) ? v.each {|k1,v1| result[ [k.to_s, k1.to_s].join('_') ] = v1 } : result[k] = v}
result # {:name=>"abc", "school_name"=>"school name", :testScores=>[1, 2, 3, 4, 5]}
but this is incredibly messy and I'd personally be unhappy with it. Performing a manual transform on known keys is probaby better and more maintainable.
Check Facets Hash extensions.

Ruby value of a hash key?

I've got a list of values that are in a Ruby hash. Is there a way to check the value of the key and if it equals "X", then do "Y"?
I can test to see if the hash has a key using hash.has_key?, but now I need to know if hash.key == "X" then...?
Hashes are indexed using the square brackets ([]). Just as arrays. But instead of indexing with the numerical index, hashes are indexed using either the string literal you used for the key, or the symbol.
So if your hash is similar to
hash = { "key1" => "value1", "key2" => "value2" }
you can access the value with
hash["key1"]
or for
hash = { :key1 => "value1", :key2 => "value2"}
or the new format supported in Ruby 1.9
hash = { key1: "value1", key2: "value2" }
you can access the value with
hash[:key1]
This question seems to be ambiguous.
I'll try with my interpretation of the request.
def do_something(data)
puts "Found! #{data}"
end
a = { 'x' => 'test', 'y' => 'foo', 'z' => 'bar' }
a.each { |key,value| do_something(value) if key == 'x' }
This will loop over all the key,value pairs and do something only if the key is 'x'.
As an addition to e.g. #Intrepidd s answer, in certain situations you want to use fetch instead of []. For fetch not to throw an exception when the key is not found, pass it a default value.
puts "ok" if hash.fetch('key', nil) == 'X'
Reference: https://docs.ruby-lang.org/en/2.3.0/Hash.html .
How about this?
puts "ok" if hash_variable["key"] == "X"
You can access hash values with the [] operator
It seems that your question is maybe a bit ambiguous.
If “values” in the first sentence means any generic value (i.e. object, since everything in Ruby can be viewed as an object), then one of the other answers probably tells you what you need to know (i.e. use Hash#[] (e.g. hash[some_key]) to find the value associated with a key).
If, however, “values” in first sentence is taken to mean the value part of the “key, value pairs” (as are stored in hashes), then your question seems like it might be about working in the other direction (key for a given value).
You can find a key that leads to a certain value with Hash#key.
ruby-1.9.2-head :001 > hash = { :a => '1', :b => :two, :c => 3, 'bee' => :two }
=> {:a=>"1", :b=>:two, :c=>3, "bee"=>:two}
ruby-1.9.2-head :002 > a_value = :two
=> :two
ruby-1.9.2-head :003 > hash.key(a_value)
=> :b
If you are using a Ruby earlier than 1.9, you can use Hash#index.
When there are multiple keys with the desired value, the method will only return one of them. If you want all the keys with a given value, you may have to iterate a bit:
ruby-1.9.2-head :004 > hash[:b] == hash['bee']
=> true
ruby-1.9.2-head :005 > keys = hash.inject([]) do # all keys with value a_value
ruby-1.9.2-head :006 > |l,kv| kv[1] == a_value ? l << kv[0] : l
ruby-1.9.2-head :007?> end
=> [:b, "bee"]
Once you have a key (the keys) that lead to the value, you can compare them and act on them with if/unless/case expressions, custom methods that take blocks, et cetera. Just how you compare them depends on the kind of objects you are using for keys (people often use strings and symbols, but Ruby hashes can use any kind of object as keys (as long as they are not modified while they serve as keys)).
I didn't understand your problem clearly but I think this is what you're looking for(Based on my understanding)
person = {"name"=>"BillGates", "company_name"=>"Microsoft", "position"=>"Chairman"}
person.delete_if {|key, value| key == "name"} #doing something if the key == "something"
Output: {"company_name"=>"Microsoft", "position"=>"Chairman"}

Resources