Converting http_params to hash - ruby-on-rails

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"}

Related

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

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.

ruby delete hash of hash based on identical values for different keys

I have a hash of hashes like this:
authors = {"7"=> {"id"=>"60"} , "0"=> {"id"=>"60"} , "1"=> {"id"=>"99"}, "8"=> {"id"=>"99"}, "15"=> {"id"=>"19"} }
I want to merge each hash where the id of the hash in that hash is duplicated (or remove each second hash with same hash of hash id).
In this case, I want to end up with
authors = {"7"=> {"id"=>"60"} , "1"=> {"id"=>"99"}, "15"=> {"id"=>"19"}}
There are quite a few questions on sorting hashes of hashes, and I've been trying to get my head around this, but I don't see how to achieve this.
Here are two ways.
#1
require 'set'
st = Set.new
authors.select { |_,v| st.add?(v) }
#=> {"7"=>{"id"=>"60"}, "1"=>{"id"=>"99"}, "15"=>{"id"=>"19"}}
#2
authors.reverse_each.with_object({}) { |(k,v),h| h[v] = k }.
reverse_each.with_object({}) { |(k,v),h| h[v] = [k] }
#=> {"7"=>[{"id"=>"60"}], "1"=>[{"id"=>"99"}], "15"=>[{"id"=>"19"}]}
or
authors.reverse_each.to_h.invert.invert.reverse_each.to_h
Try this one
authors.to_a.uniq { |item| item.last["id"] }.to_h
=> {"7"=>{"id"=>"60"}, "1"=>{"id"=>"99"}, "15"=>{"id"=>"19"}}
uniq method with a block can do the work

How to get each value from hash of array?

I have a hash of array with key, values in ruby which I want to pass each value with key. I want to extract content using my hash. My code is :
def get_content
GetPageContent.new(#pdf.id, session[:selected_pages][#document.id.to_s])
end
in session[:selected_pages] I got like this
{"15"=>["001"], "24"=>["001","005"]}
In this first value is pdf id and second value is page number of that pdf.
I want this :
GetPageContent.new(#pdf.id, session[:selected_pages][#document.id.to_s])
I want to pass all values like this:
GetPageContent.new(15, 001)
GetPageContent.new(24, 001)
GetPageContent.new(24, 005)
How to map each key values from hash of array?
A simple nested loop will suffice:
session[:selected_pages].each do |pdf_id, page_numbers|
page_numbers.each { |page_number| GetPageContent.new(pdf_id, page_number) }
end
You can use something like this
hsh = {"15"=>["001"], "24"=>["001","005"]}
hsh.each(&->(page, pages){pages.each{|id| GetPageContent.new(id, page)}})
You could do something like this to get a simple array. h is your hash
pages = h.flat_map { |key, arr| ([key] * arr.size).zip(arr) }
=> [["15", "001"], ["24", "001"], ["24", "005"]]
pages.map! { |pdf_id, page_number| GetPageContent.new(pdf_id, page_number) }
session[:selected_pages]
.map{|k, v| [k].product(v)}.flatten(1)
# => [["15", "001"], ["24", "001"], ["24", "005"]]
The rest is up to you.

Iterate through array to construct hash in Ruby

Currently I have:
cache_hash = {}
array = [:id, :content, :title]
data_source = some_active_record_object
array.each{ |k| cache_hash[k] = data_source.send(k) }
cache_hash
#=>{:id=>"value1", :content=>"value2", :title=>"value3"}
I'm wondering if there is a better way to iterate through the array and get the hash out.
Write as below :
cache_hash = Hash[array.map { |k| [k, data_source.send(k)] }]
Or use new #to_h
cache_hash = array.map { |k| [k, data_source.send(k)] }.to_h
I'm not sure if you want to follow this route, but here is an alternative with AR query:
Foo.select('id, content, title').to_a.map(&:serializable_hash)
Foo is the model you're operating on.
This is similar to #Arup's answer using map. The cool thing about map functions, in any programming language (not just Ruby), is that you can also express them in terms of
an inject function (also called fold, reduce, or aggregate in other languages):
cache_hash = array.inject({}) do |hash, key|
hash[key] = data_source.send key
hash
end
Not as clear as Arup's answer using map, but kind of cool to know anyways.

Ruby on Rails: Array to Hash with (key, array of values)

Lets say I have an Array of content_categories (content_categories = user.content_categories)
I now want to add every element belonging to a certain categorie to content_categories with the category as a key and the the content-item IDs as elements of a set
In PHP something like this is possible:
foreach ($content_categories as $key => $category) {
$contentsByCategoryIDArray = Category.getContents($category[id])
$content_categories[$key][$contentsByCategoryIDArray]
}
Is there an easy way in rails to do this?
Greets,
Nico
Your question isn't really a Rails question, it's a general Ruby programming question.
Your description isn't very clear, but from what I understand, you want to group IDs for common categories using a Hash. There are various other ways of doing this, but this is easy to understand::
ary = [
'cat1', {:id => 1},
'cat2', {:id => 2},
'cat1', {:id => 3}
]
hsh = {}
ary.each_slice(2) { |a|
key,category = a
hsh[key] ? hsh[key] << category[:id] : hsh[key] = [category[:id]]
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
I'm using a simple Array with a category, followed by a simple hash representing some object instance, because it makes it easy to visualize. If you have a more complex object, replace the hash entries with those objects, and tweak how you access the ID in the ternary (?:) line.
Using Enumerable.inject():
hsh = ary.each_slice(2).inject({}) { |h,a|
key,category = a
h[key] ? h[key] << category[:id] : h[key] = [category[:id]]
h
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
Enumerable.group_by() could probably shrink it even more, but my brain is fading.
I'd use Enumerable#inject
content_categories = content_categories_array.inject({}){ |memo, category| memo[category] = Category.get_contents(category); memo }
Hash[content_categories.map{|cat|
[cat, Category.get_contents(cat)]
}]
Not really the right answer, because you want IDs in your array, but I post it anyway, because it's nice and short, and you might actually get away with it:
content_categories.group_by(&:category)
content_categories.each do |k,v|
content_categories[k] = Category.getContents(v)
end
I suppose it's works
If i understand correctly, content_categories is an array of categories, which needs to be turned into a hash of categories, and their elements.
content_categories_array = content_categories
content_categories_hash = {}
content_categories_array.each do |category|
content_categories_hash[category] = Category.get_contents(category)
end
content_categories = content_categories_hash
That is the long version, which you can also write like
content_categories = {}.tap do |hash|
content_categories.each { |category| hash[category] = Category.get_contents(category) }
end
For this solution, content_categories must be a hash, not an array as you describe. Otherwise not sure where you're getting the key.
contents_by_categories = Hash[*content_categories.map{|k, v| [k, Category.getContents(v.id)]}]

Resources