Normally I have:
cookies[:location] = { :value => { :city => 'foo', :country => 'bar' } }
However, sometimes :country is not set. When I do:
cookies[:location][:country].present?
to check if country has been set, it returns an error:
[:country] is not a symbol
How would I check to see if a country is set in cookie[:location] if I cannot do it this way?
You cookies hash is nested differently than you are expecting: :value is nested under :location, :city and :country are nested under :value. Here is some irb output to get you started:
[~]$ irb
>> cookies={}
=> {}
>> cookies[:location] = { :value => { :city => 'foo', :country => 'bar' } }
=> {:value=>{:city=>"foo", :country=>"bar"}}
>> cookies[:location][:value][:country]
=> "bar"
>> cookies.to_s
=> "{:location=>{:value=>{:city=>\"foo\", :country=>\"bar\"}}}"
If you nest the hash like below, you will get your expected behavior:
>> cookies[:location] = { :city => 'foo', :country => 'bar' }
=> {:city=>"foo", :country=>"bar"}
>> cookies[:location][:country]
=> "bar"
Related
I have been trying for ages. I am trying to round an array with hashes.
[
{
:email => "user_01#yorlook.com",
:amount => 129.22500000000002
},
{
:email => "user_02#yorlook.com",
:amount => 112.67500000000001
}
]
I need a method to make it look like this:
[
{
:email => "user_01#yorlook.com",
:amount => 129.23
},
{
:email => "user_02#yorlook.com",
:amount => 112.68
}
]
Thanks for your help in advance!
Assuming arr always contains Hash with key amount which is a Float (as in example).
arr.each { |hash|
hash[:amount] = hash[:amount].round(2)
}
#=> [{:email=>"user_01#yorlook.com", :amount=>129.23}, {:email=>"user_02#yorlook.com", :amount=>112.68}]
I have 2 hashes like so:
stored_hash = {
:name => "hash_1",
:version => "1.0",
:values => {
:value_1 => "some_value",
:value_2 => "some_other_value",
:value_3 => "a_new_value",
...
:value_x => "some_x_value"
}
}
compare_hash = {
:name => "hash_2",
:version => "2.0",
:values => {
:different_1 => "some_value",
:different_2 => "some_other_value",
:different_3 => "a_new_value",
...
:different_x => "some_x_value"
}
}
I find the common values in the :values key of both the hashes like this :
same_values = hash[:values].values & compared_hash[:values].values
And once I get it, I want to return a new hash similar to 'stored_hash', but with the :values of it containing the same_values I found before.
For example, if both hashes have "some_value", "some_other_value", "a_new_value", my new hash should look like :
new_hash = {
:name => "hash_1",
:version => "1.0",
:values => {
:value_1 => "some_value",
:value_2 => "some_other_value",
:value_3 => "a_new_value"
}
}
This should work for you :
stored_hash = {
:name => "hash_1",
:version => "1.0",
:values => {
:value_1 => "some_value",
:value_2 => "some_other_value",
:value_3 => "a_new_value",
:value_x => "some_a_value"
}
}
compare_hash = {
:name => "hash_2",
:version => "2.0",
:values => {
:different_1 => "some_value",
:different_2 => "some_other_value",
:different_3 => "a_new_value",
:different_x => "some_b_value"
}
}
common_values = compare_hash[:values].values & stored_hash[:values].values
new_hash = stored_hash.dup
new_hash[:values] = {}
common_values.each_with_index do |value, index |
new_hash[:values]["value_#{index+1}".to_sym] = value
end
new_hash
# => new_hash = {
# :name => "hash_1",
# :version => "1.0",
# :values => {
# :value_1 => "some_value",
# :value_2 => "some_other_value",
# :value_3 => "a_new_value"
# }
# }
This is one way you could obtain your desired result.
Code
require 'set'
def make_new_hash(stored_hash, compare_hash)
new_hash = stored_hash.dup
compare_values =
(stored_hash[:values].values & compare_hash[:values].values).to_set
values_hash = new_hash[:values]
keys_to_keep = values_hash.keys.select { |k|
compare_values.include?(values_hash[k])}
new_hash[:values] =
Hash[keys_to_keep.zip(values_hash.values_at(*keys_to_keep))]
new_hash
end
Example
stored_hash = {
:name => "hash_1",
:version => "1.0",
:values => {
:value_1 => "some_value",
:value_2 => "some_other_value",
:value_3 => "a_new_value",
:value_x => "some_x_value"
}
}
compare_hash = {
:name => "hash_2",
:version => "2.0",
:values => {
:different_1 => "some_value",
:different_2 => "a_new_value",
:different_3 => "some_strange_value",
:different_x => "some_x_value"
}
}
Note that I've made a small change in stored_hash from that given in the question.
make_new_hash(stored_hash, compare_hash)
#=> {:name=>"hash_1", :version=>"1.0",
# :values=>{:value_1=>"some_value", :value_3=>"a_new_value",
# :value_x=>"some_x_value"}}
Explanation
Create a copy of stored_hash:
new_hash = stored_hash.dup
#=> {:name=>"hash_1", :version=>"1.0",
# :values=>{:value_1=>"some_value",
# :value_2=>"some_other_value",
# :value_3=>"a_new_value",
# :value_x=>"some_x_value"}}
We only want to keep a key k of new_hash[:values] if new_hash[:values][k] is one of the values in both the hash stored_hash[:values] and the hash compare_hash[:values], so we obtain those values:
compare_values =
(stored_hash[:values].values & compare_hash[:values].values).to_set
# => #<Set: {"some_value", "a_new_value", "some_x_value"}>
I chose to save them in a set both to speed lookup and to obtain unique values. They could alternatively be saved to array like so:
(stored_hash[:values].values & compare_hash[:values].values).uniq
#=> ["some_value", "a_new_value", "some_x_value"]
The code that follows is the same if an array is used rather than a set.
To simpify, let's create a variable:
values_hash = new_hash[:values]
#=> {:value_1=>"some_value", :value_2=>"some_other_value",
# :value_3=>"a_new_value", :value_x=>"some_x_value"}
Next determine the keys of New_hash[:values] we wish to keep:
keys_to_keep = values_hash.keys.select { |k|
compare_values.include?(values_hash[k])}
#=> [:value_1, :value_3, :value_x]
All keys other than :value_2 are kept. That key is not kept because comparative_hash[:values] does not have a value "some_other_value".
We can now construct the updated value of new_hash[:values]:
new_hash[:values] =
Hash[keys_to_keep.zip(values_hash.values_at(*keys_to_keep))]
#=> {:value_1=>"some_value", :value_3=>"a_new_value",
# :value_x=>"some_x_value"}
Lastly, we return new_hash:
new_hash
#=> {:name=>"hash_1", :version=>"1.0",
# :values=>{:value_1=>"some_value", :value_3=>"a_new_value",
# :value_x=>"some_x_value"}}
Let's confirm that stored_hash will not be changed when a value of new_hash[:values] is changed:
new_hash[:values][:value_1] = 'cat'
new_hash
#=> {:name=>"hash_1", :version=>"1.0",
# :values=>{:value_1=>"cat", :value_3=>"a_new_value",
# :value_x=>"some_x_value"}}
stored_hash
#=> {:name=>"hash_1", :version=>"1.0",
# :values=>{:value_1=>"some_value", :value_2=>"some_other_value",
# :value_3=>"a_new_value", :value_x=>"some_x_value"}}
I'll try to keep this simple, my previous wording was maybe a bit too verbose:
Here is the example Omniauth hash: https://github.com/mkdynamic/omniauth-facebook
I can access and save some values from this but not others. The field is writable, so I know its just my syntax (beginner, sorry!)
{
:provider => 'facebook',
:uid => '1234567',
:info => {
:nickname => 'jbloggs',
:email => 'joe#bloggs.com',
:name => 'Joe Bloggs',
:first_name => 'Joe',
:last_name => 'Bloggs',
:image => 'http://graph.facebook.com/1234567/picture?type=square',
:urls => { :Facebook => 'http://www.facebook.com/jbloggs' },
:location => 'Palo Alto, California',
:verified => true
},
:credentials => {
:token => 'ABCDEF...', # OAuth 2.0 access_token, which you may wish to store
:expires_at => 1321747205, # when the access token expires (it always will)
:expires => true # this will always be true
},
:extra => {
:raw_info => {
:id => '1234567',
:name => 'Joe Bloggs',
:first_name => 'Joe',
:last_name => 'Bloggs',
:link => 'http://www.facebook.com/jbloggs',
:username => 'jbloggs',
:location => { :id => '123456789', :name => 'Palo Alto, California' },
:gender => 'male',
:email => 'joe#bloggs.com',
:timezone => -8,
:locale => 'en_US',
:verified => true,
:updated_time => '2011-11-11T06:21:03+0000'
}
}
}
I can do this to get gender and save it.
location:auth.extra.raw_info["gender"]
Obviously though I dont want to save gender to location. I want to get "Palo Alto" and save it. But this doesn't work.
location.auth.extra.raw_info["location"]["name"]
What am I doing wrong? When I try it in console, I'm able to get the value.
try this
location.auth.extra.raw_info.location.name
or this
location.auth.extra.raw_info[:location][:name]
Yeah, what you suggested was what I was trying...and it turns out we were right but FB had changed how that hash was set up so it wasn't working. Lesson learned: subscribe FB's notifications next time :)
I'm trying to retrieve data in a nested form from the following two tables (in SQLite)
DB = Sequel.sqlite('database.sqlite')
DB.create_table? :artists do
primary_key :id
String :name
end
DB.create_table? :albums do
primary_key :id
String :title
foreign_key :artist_id,
:artists,
:key => :id
end
artists = DB[:artists]
albums = DB[:albums]
id1 = artists.insert(:name => 'Mike')
id2 = artists.insert(:name => 'John')
albums.insert(:title => 'Only You', :artist_id => id1 )
albums.insert(:title => 'Only Us', :artist_id => id1 )
albums.insert(:title => 'Only Me', :artist_id => id2 )
The output I'm trying to get -
[
{
:id => 1,
:name => 'Mike'
:albums => [
{
:id => 1,
:title => 'Only You'
},
{
:id => 2,
:title => 'Only Us'
}
]
},
{
:id => 2,
:name => 'John'
:albums => [
{
:id => 3,
:title => 'Only Me'
}
]
}
]
I've tried 'eager' loading -
class Artist < Sequel::Model(:artists)
one_to_many :db[:albums], :key => :artist_id
end
class Album < Sequel::Model(:albums)
many_to_one :artist, :key => :artist_id
end
Artist.eager(:albums).all{ |a| p a }
But that didn't work.
Can anyone point me in the right direction?
Artist.eager(:albums).all does eagerly load the albums, but {|a| p a} is not going to show the albums (as Sequel::Model#inspect only shows values for the current model, not any associated objects). Use {|a| p [a, a.albums]} to see that the albums are already loaded.
If you want to produce the hash you described:
Artist.eager(:albums).all.map do |a|
a.values.merge(:albums=>a.albums.map{|al| al.values})
end
You can add a method to Artist to output it the way you want it
class Artist < Sequel::Model(:artists)
one_to_many :albums, :key => :artist_id
def my_hash
to_hash.merge!(
{
:albums => albums.map{|a|
a.to_hash.reject!{ |k,v|
k==:artist_id
}
}
}
)
end
end
class Album < Sequel::Model(:albums)
many_to_one :artist, :key => :artist_id
end
records = Artist.all.map{ |a| a.my_hash }
p records
Instead of using reject! it would be cleaner to add a my_hash method the Album to return a hash without the :artist_id, but you get the idea. This outputs:
[
{
:albums=>[
{
:title=>"Only You",
:id=>1
},
{
:title=>"Only Us",
:id=>2
}
],
:name=>"Mike",
:id=>1
},
{
:albums=>[
{
:title=>"Only Me",
:id=>3
}
],
:name=>"John",
:id=>2
}
]
I'm playing around with nested hashes and I'm trying to figure out how to fetch multiple keys when my hash is a nested one:
imahash = { :id => { :name => "Alma", :email => "alma#mail.com" },
:stats => { :gender => "Female" },
:location => { :city => "Freeport", :state => "Maine" }
}
I know how to retrieve the nested value, and typing in the hash name will dump all the keys and values. But what I want to do is to fetch specific keys, such as :name and :gender only. Or :name and :city only.
Is this possible? Because from what I've found, it seems that you can only retrieve hash values for one key at a time or for all the keys at once.
My desired output would be something like:
=> { :id => { :name => "Alma" }, :location => { :city => "Freeport" } }
I presume you want to grab the values out in a tuple? You can make an array that contains whatever collection of values you want.
Try the following for name and city:
[imahash[:id][:name], imahash[:location][:city]]
=> ["Alma", "Freeport"]
Not exactly sure what you're asking here, but it seems like you're wanting to create a new hash from the bigger one.
To fetch specific keys like :name and :gender only
name_and_gender_hash = {
:name => imahash[:id][:name],
:gender => imahash[:stats][:gender]
}
would result in
{:name => "Alma", :gender => "female"}