Re-ordering hash items to have some appear at the end - ruby-on-rails

I'm returning a hash of form inputs, which at the moment looks like:
hash = {
"body"=>:text,
"button_text"=>:string,
"category"=>:integer,
"dashboard"=>:boolean,
"feature"=>:integer,
"featured_from"=>:datetime,
"featured_to"=>:datetime,
"global"=>:boolean,
"hyperlink"=>:string,
"jobs"=>:boolean,
"labs"=>:boolean,
"leaderboard"=>:boolean,
"management"=>:boolean,
"news"=>:boolean,
"objectives"=>:boolean,
"only_for_enterprise_users"=>:boolean,
"published_at"=>:datetime,
"title"=>:string,
"workflow_state"=>:string
}
I need to place the following keys at the end:
["dashboard", "jobs", "labs", "management", "news", "objectives", "global"]
Which will leave me with:
{
"body"=>:text,
"button_text"=>:string,
"category"=>:integer,
"feature"=>:integer,
"featured_from"=>:datetime,
"featured_to"=>:datetime,
"hyperlink"=>:string,
"only_for_enterprise_users"=>:boolean,
"published_at"=>:datetime,
"title"=>:string,
"workflow_state"=>:string,
"dashboard"=>:boolean,
"jobs"=>:boolean,
"labs"=>:boolean,
"leaderboard"=>:boolean,
"management"=>:boolean,
"news"=>:boolean,
"objectives"=>:boolean,
"global"=>:boolean
}
All the links I've found relate to transforming keys / values without re-ordering, and outside of manually deleting each key and then reinserting I can't see another way of getting my desired output.
Is there an easy way to achieve what I need?
Thanks in advance

You can try following,
(hash.keys - end_keys + end_keys).map { |key| [key, hash[key]] }.to_h

hash = { "body"=>:text, "button_text"=>:string, "category"=>:integer,
"dashboard"=>:boolean, "feature"=>:integer }
enders = ["button_text", "dashboard"]
hash.dup.tap { |h| enders.each { |k| h.update(k=>h.delete(k)) } }
See Object#tap, Hash#update (aka merge!) and Hash#delete.
dup may of course be removed if hash can be mutated, which may be reasonable as only the keys are being reordered.

Related

Rails: How to update the hash in an array

How to update a hash from an array?
Sample Data
form_fields = [
{
key: '1',
properties: null
},
{
key: '2',
properties: {"options"=>[{"label"=>"Europe", "value"=>"europe"}, {"label"=>"North America", "type"=>"groupstart"}, {"label"=>"Canada", "value"=>"canada"}, {"label"=>"USA", "value"=>"usa"}, {"label"=>"", "type"=>"groupend"}]}
}
]
Code I have so far
form_fields = form_fields.map {
|field| {
field.properties = field.properties ? JSON.parse(field.properties) : {}
}
I built this based on some other questions I came across such as this one How do I modify an array while I am iterating over it in Ruby?
The syntax for the map is very close to what you already have, the correct syntax would be like this:
form_fields = form_fields.map {
|field|
...
}
To access a object in the Hash structure you would use the symbol, or string as the selector, like this:
field[:properties]
field[:properties]["options"]
The code you have in your map, does not really make sense. The object stored in the code you provided is already ruby code, the hash keys is just strings instead of symbols.
You do also not want to update your form_fields variable to the result of your map, since that will overwrite your array, and only keep the last element in the array.
I believe what you want is something like this:
form_fields.map { |field|
field[:properties] = field[:properties] ? field[:properties] : {}
}
That will turn your array from:
[{:key=>"1", :properties=>nil}, {:key=>"2", :properties=>{ ... }}]
to:
[{:key=>"1", :properties=>{}}, {:key=>"2", :properties=>{ ... }}]

Simplifying an expression using .map

Below I have an example active record query using a bunch of each iterators
user.user_spells.each do |us|
us.spell.buff_effects.where(stat_effected:'gold').each do |be|
value = value + be.value
end
end
I would like to use .map to return a list of all the results so I can do it essentially in one line.
I came up with:
user.user_spells.map { |us| us.spell.buff_effects.where(stat_effected:stat_effected) }.each do |be|
value = value + be.value
end
However... the .map block returns some empty arrays. Not sure how to write it correctly.
Any help would be appreciated! Thanks
Probably along these lines, if what you want is the sum of values in the end:
value =
user.user_spells.flat_map do |us|
us.spell.buff_effects.where(stat_effected:'gold').map(&:value)
end.reduce(&:+)

Ruby simple map object to has not working

I have a simple problem but I cannot find a solution.
I have a Forum model (active record) with several fields.
I'm creating a class method that return to me an has with one value as key (not the id) and the other as value.
This is my method:
Forum.all.map { |f| [f.old_id => f.icon.url(:micro) ]}
It returns
[[{10=>"/images/fallback/icon_fallback.png"}],
[{6=>"/images/fallback/icon_fallback.png"}],
[{18=>"/images/fallback/icon_fallback.png"}]]
instead of
{10=>"/images/fallback/icon_fallback.png", 6=>"/images/fallback/icon_fallback.png", 18=>"/images/fallback/icon_fallback.png"}
What is the error?
In your code, map returns an array and the square brackets produce arrays containing hashes.
res = {}
Forum.all{|f| res[f.old_id] = f.icon.url(:micro) }
in short you can just modify like this, change square brackets to curly brackets:
Forum.all.inject({}) { |r,f| r.merge!(f.old_id => f.icon.url(:micro)) }
You can make a minimal change to your code and receive your needed result by using to_h:
Forum.all.map { |f| [f.old_id, f.icon.url(:micro)] }.to_h
Yes, you can use reduce or inject method, or just construct the Hash from Arrays:
Hash[Forum.all.map { |f| [f.old_id, f.icon.url(:micro) ]}]
for ruby-2.0 you can use #to_h method:
Forum.all.map { |f| [f.old_id, f.icon.url(:micro) ]}.to_h
use active supports each_with_object:
Forum.all.each_with_object({}) { |f, h| h[f.old_id] = f.icon.url(:micro) }

Converting http_params to hash

I can obtain an array from the string
http_params="created_end_date=2013-02-28&created_start_date=2013-01-01&page_size=50&offset=0&order_id=0D1108211501118%0D%0A0D11108211501118%0D%0Ac%0D%0AD%0D%0ADK212071409743%0D%0AKK30109110100%0D%0AKK30111140300%0D%0AKK30111140400%0D%0AKK30115120100%0D%0AKK30115150100&page_number=1"
So I did myarray=http_params.split("&"):
myarray=["created_end_date=2013-02-28", "created_start_date=2013-01-01", "page_size=50", "offset=0", "order_id=0D1108211501118%0D%0A0D11108211501118%0D%0Ac%0D%0AD%0D%0ADK212071409743%0D%0AKK30109110100%0D%0AKK30111140300%0D%0AKK30111140400%0D%0AKK30115120100%0D%0AKK30115150100", "page_number=1"]
I need to convert this to a hash myhash, so that I can make a Rest Client post call for myhash.to_json. Basically it should be key,value pairs like:
{:created_end_date=>"2013-02-28",:created_start_date=>"2013-01-01"....}
I know that the inverse operation can be done like this:
http_params = myhash.map{|k,v| "#{k}=#{v}"}.join('&')
but I am unable to come up with neat code for this.
What's the best way I should go about this?
require 'cgi'
hash = CGI::parse http_params
Or you can use:
hash = Rack::Utils.parse_nested_query http_params
Which does not return the values as arrays.
With pure Ruby methods, you can convert your string into a Hash as follows:
"a=1&b=2".split('&').map { |h| Hash[*h.split("=")] }
=> [{"a"=>"1"}, {"b"=>"2"}]
A blog post how to operate on Ruby collections is here: http://thinkingonthinking.com/map-reduce-in-ruby/
To get symbols as keys, a small additional step is necessary:
"a=1&b=2".split('&').map { |h| hs = h.split("="); Hash[hs[0].to_sym, hs[1]] }
=> [{:a=>"1"}, {:b=>"2"}]
As last step, a merge of the inner Hash elements has to be done. This can be done like:
"a=1&b=2".split('&').map { |h| hs = h.split("="); Hash[hs[0].to_sym, hs[1]] }.inject({}) { |s, h| s.merge(h) }
=> {:a=>"1", :b=>"2"}

search for key in a nested hash in Rails

I have the following nested hash (from Ominauth-Facebook) captured in an object called myAuth
<Ominauth::AuthHash credentials
extra=#<Hashie:: Mash
raw_info=#<Hashie::Mash email="myemail#gmail.com">>>
I would like to extract email, so I use:
myAuth['extra']['raw_info']['email']
However, I would like to search the entire hash and get the value for key email without knowing exact hash structure. How should I go about it?
Thank you.
Don't know if this is the best solution, but i would do:
h = {seal: 5, test: 3, answer: { nested: "damn", something: { email: "yay!" } } }
def search_hash(h, search)
return h[search] if h.fetch(search, false)
h.keys.each do |k|
answer = search_hash(h[k], search) if h[k].is_a? Hash
return answer if answer
end
false
end
puts search_hash(h, :email)
This will return the value if the key exists or false.

Resources