I am a newbie in Ruby and I am trying to learn Ruby these day. I was going through Hash today and got stuck in problem related to Hash
I have the following Hash
{"key1" => ["param_1","param_2"], "key2" => ["param_3","param_4"], "key3" => "param_5", "key4" => "param_6","key5" => ["param_7","param_8"]}
and I want to convert the above Hash into the following.
{"my_hash" => [ {"name" => "key1","value" => ["param_1","param_2"]},
{"name" => "key2","value" => ["param_3","param_4"]},
{"name" => "key3","value" => ["param_5"]},
{"name" => "key4","value" => ["param_6"]},
{"name" => "key5","value" => ["param_7","param_8"]}
]
}
Can someone show me how can i do it in Ruby in a efficient way.
hsh = {"key1" => ["param_1","param_2"],
"key2" => ["param_3","param_4"], "key3" => "param_5",
"key4" => "param_6","key5" => ["param_7","param_8"]}
hsh.map{|k,v| {name: k,value: Array(v) }}
# => [{:name=>"key1", :value=>["param_1", "param_2"]},
# {:name=>"key2", :value=>["param_3", "param_4"]},
# {:name=>"key3", :value=>["param_5"]},
# {:name=>"key4", :value=>["param_6"]},
# {:name=>"key5", :value=>["param_7", "param_8"]}]
hsh = {"key1" => ["param_1","param_2"],
"key2" => ["param_3","param_4"], "key3" => "param_5",
"key4" => "param_6","key5" => ["param_7","param_8"]}
hsh.map{|k,v| {"name" => k,"value" => Array(v) }}
# => [{"name"=>"key1", "value"=>["param_1", "param_2"]},
# {"name"=>"key2", "value"=>["param_3", "param_4"]},
# {"name"=>"key3", "value"=>["param_5"]},
# {"name"=>"key4", "value"=>["param_6"]},
# {"name"=>"key5", "value"=>["param_7", "param_8"]}]
Related
I'm integrating with a 3rd party service for which the complete URL string of a GET request needs to look something like:
<URL>/some_endpoint?JsonData={"key1" => "value1", "key2" => "value2", "key3" => "value3"}
To be clear, this request has only one param, JsonData and the entire hash is the corresponding value. All values inside the hash are URL-safe.
JSON/CGI/URI encodings don't seem to help here.
How do I not encode/add escape characters here?
Perhaps you're looking for the to_query method?
2.3.1 :024 > {"key1" => "value1", "key2" => "value2", "key3" => "value3"}.to_query
=> "key1=value1&key2=value2&key3=value3"
Or:
2.3.1 :029 > {"JsonData" => {"key1" => "value1", "key2" => "value2", "key3" => "value3"}}.to_query
=> "JsonData%5Bkey1%5D=value1&JsonData%5Bkey2%5D=value2&JsonData%5Bkey3%5D=value3"
Or, as stated in the docs,
An optional namespace can be passed to enclose key names:
2.3.1 :030 > {"key1" => "value1", "key2" => "value2", "key3" => "value3"}.to_query('JsonData')
=> "JsonData%5Bkey1%5D=value1&JsonData%5Bkey2%5D=value2&JsonData%5Bkey3%5D=value3"
So, it could look something like
2.3.1 :031 > "<URL>/some_endpoint?#{{"key1" => "value1", "key2" => "value2", "key3" => "value3"}.to_query('JsonData')}"
=> "<URL>/some_endpoint?JsonData%5Bkey1%5D=value1&JsonData%5Bkey2%5D=value2&JsonData%5Bkey3%5D=value3"
How about manually creating the string, like this:
hash = {"key1" => "value1", "key2" => "value2", "key3" => "value3"}
url = "/some_endpoint?JsonData={" + hash.map { |k, v| "'#{k}' => '#{v}'" }.join(", ") + "}"
#=> "/some_endpoint?JsonData={'key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3'}"
This is quite embarassing. Converting the hash to JSON did work eventually.
The final URL looks like:
hash = {"key1" => "value1", "key2" => "value2", "key3" => "value3"}
"#{URL}/some_endpoint?JsonData=#{hash.to_json}"
I have long list of values in the inner_value field from which I want only some values
I have array in this format:
hash_array = [
{
"array_value" => 1,
"inner_value" => [
{"iwantthis" => "forFirst"},
{"iwantthis2" => "forFirst2"},
{"Idontwantthis" => "some value"},
{"iwantthis3" => "forFirst3"},
{"Idontwantthis2" => "some value"},
{"Idontwantthis3" => "some value"},
{"Idontwantthis4" => "some value"},
{"Idontwantthis5" => "some value"},
{"Idontwantthis6" => "some value"},
]
},
{
"array_value" => 2,
"inner_value" => [
{"iwantthis" => "forSecond"},
{"Idontwantthis" => "some value"},
{"iwantthis3" => "forSecond3"},
{"iwantthis2" => "forSecond2"},
{"Idontwantthis2" => "some value"},
{"Idontwantthis3" => "some value"},
{"Idontwantthis4" => "some value"},
{"Idontwantthis5" => "some value"},
{"Idontwantthis6" => "some value"},
]
},
]
Desired Output:
[
{
"array_value" => 1,
"inner_value" => [
{"iwantthis" => "forFirst"},
{"iwantthis2" => "forFirst2"},
{"iwantthis3" => "forFirst3"}
]
},
{
"array_value" => 2,
"inner_value" => [
{"iwantthis" => "forSecond"},
{"iwantthis2" => "forSecond2"},
{"iwantthis3" => "forSecond3"}
]
},
]
I have tried using running loop in this but its too much costly.
So I tried something like this:
hash_array.select { |x| x["inner_value"].select {|y| !y["iwantthis"].nil? } }
but this ain't working either..
Note:Order/Sort does not matter
Your aim is not to select, you have to modify the input:
hash_array.map { |hash| hash['inner_value'] = hash['inner_value'].first }
#=> [
# {
# "array_value"=>1,
# "inner_value"=> {
# "iwantthis"=>"forFirst"
# }
# },
# {
# "array_value"=>2,
# "inner_value"=> {
# "iwantthis"=>"forSecond"
# }
# }
# ]
Here you'd basically change the value of whole hash['inner_value'] to what you want.
To do this with known key:
hash_array.map do |hash|
hash['inner_value'] = hash['inner_value'].find { |hash| hash['iwantthis'] }
end # `iwantthis` is the key, that can change
For multiple keys:
keys = %w(iwantthis Idontwantthis)
hash_array.map do |hash|
hash['inner_value'] = keys.flat_map do |key|
hash['inner_value'].select {|hash| hash if hash[key] }
end
end
#=> [{"array_value"=>1, "inner_value"=>[{"iwantthis"=>"forFirst"}, {"Idontwantthis"=>"some value"}]}, {"array_value"=>2, "inner_value"=>[{"iwantthis"=>"forSecond"}, {"Idontwantthis"=>"some value"}]}]
you can use map
hash_array.map{|k| {"array_value" => k['array_value'], 'inner_value' => k['inner_value'][0]} }
#=> [{"array_value"=>1, "inner_value"=>{"iwantthis"=>"forFirst"}}, {"array_value"=>2, "inner_value"=>{"iwantthis"=>"forSecond"}}]
I'm trying to setup the smart_listing gem in my app. Where and how can I configure the default per page number of pagination results. In the docs of smart_listing is mentioned that it uses kaminari.
If you don't already have a config/initializers/kaminari_config.rb file, run Kaminari's config generator:
bundle exec rails generate kaminari:config
This will create config/initializers/kaminari_config.rb with the default content:
Kaminari.configure do |config|
# config.default_per_page = 25
# config.max_per_page = nil
# config.window = 4
# config.outer_window = 0
# config.left = 0
# config.right = 0
# config.page_method_name = :page
# config.param_name = :page
end
Simply uncomment and edit the configuration options you are interested in changing.
Update:
SmartListing provides it's own configuration options for pagination in config/initializers/smart_listing.rb:
SmartListing.configure do |config|
config.global_options({
#:param_names => { # param names
#:page => :page,
#:per_page => :per_page,
#:sort => :sort,
#},
#:array => false, # controls whether smart list should be using arrays or AR collections
#:max_count => nil, # limit number of rows
#:unlimited_per_page => false, # allow infinite page size
#:paginate => true, # allow pagination
#:memorize_per_page => false, # save per page settings in the cookie
#:page_sizes => DEFAULT_PAGE_SIZES, # set available page sizes array
#:kaminari_options => {:theme => "smart_listing"}, # Kaminari's paginate helper options
})
config.constants :classes, {
#:main => "smart-listing",
#:editable => "editable",
#:content => "content",
#:loading => "loading",
#:status => "smart-listing-status",
#:item_actions => "actions",
#:new_item_placeholder => "new-item-placeholder",
#:new_item_action => "new-item-action",
#:new_item_button => "btn",
#:hidden => "hidden",
#:autoselect => "autoselect",
#:callback => "callback",
#:pagination_per_page => "pagination-per-page text-center",
#:pagination_count => "count",
#:inline_editing => "info",
#:no_records => "no-records",
#:limit => "smart-listing-limit",
#:limit_alert => "smart-listing-limit-alert",
#:controls => "smart-listing-controls",
#:controls_reset => "reset",
#:filtering => "filter",
#:filtering_search => "glyphicon-search",
#:filtering_cancel => "glyphicon-remove",
#:filtering_disabled => "disabled",
#:sortable => "sortable",
#:icon_new => "glyphicon glyphicon-plus",
#:icon_edit => "glyphicon glyphicon-pencil",
#:icon_trash => "glyphicon glyphicon-trash",
#:icon_inactive => "glyphicon glyphicon-circle",
#:icon_show => "glyphicon glyphicon-share-alt",
#:icon_sort_none => "glyphicon glyphicon-resize-vertical",
#:icon_sort_up => "glyphicon glyphicon-chevron-up",
#:icon_sort_down => "glyphicon glyphicon-chevron-down",
}
config.constants :data_attributes, {
#:main => "smart-listing",
#:confirmation => "confirmation",
#:id => "id",
#:href => "href",
#:callback_href => "callback-href",
#:max_count => "max-count",
#:inline_edit_backup => "smart-listing-edit-backup",
#:params => "params",
#:observed => "observed",
#:href => "href",
#:autoshow => "autoshow",
#:popover => "slpopover",
}
config.constants :selectors, {
#:item_action_destroy => "a.destroy",
#:edit_cancel => "button.cancel",
#:row => "tr",
#:head => "thead",
#:filtering_icon => "i"
}
end
Uncomment the page_sizes line and replace DEFAULT_PAGE_SIZES with an array like [10, 20, 50, 100]
No need to comment out page_sizes in initializers/smart_listing.rb.
You can just define per_page value in controller, eg:
users_scope = User.all.includes(:bio)
users_scope = users_scope.like(params[:filter]) if params[:filter]
#users = smart_listing_create :users, users_scope, partial: "users/list", page_sizes: [5, 7, 13, 26]
smart_listing v1.1.2
I am using Ruby on Rails 4.1 and I would like to permit the following incoming parameters by using the StrongParameters gem:
# Parameters:
{
"my_key" => {
"one" => {
"0" => { "a" => "a_value", "b" => "b_value"},
"1" => { "a" => "a_value", "b" => "b_value"},
"2" => { "a" => "a_value", "b" => "b_value"}
},
"two" => {
"0" => { "c" => "c_value", "d" => "d_value"},
"1" => { "c" => "c_value", "d" => "d_value"},
"2" => { "c" => "c_value", "d" => "d_value"}
}
}
}
In controller I tried
params
.require(:my_key)
.permit(
[
:one => [
"0" => [:a, :b],
"1" => [:a, :b],
"2" => [:a, :b]
],
:two => [
"0" => [:c, :d],
"1" => [:c, :d],
"2" => [:c, :d]
]
]
)
and
params
.require(:my_key)
.permit(
{
:one => {
"0" => [:a, :b],
"1" => [:a, :b],
"2" => [:a, :b]
},
:two => {
"0" => [:c, :d],
"1" => [:c, :d],
"2" => [:c, :d]
}
}
)
But I get the error
ActionController::UnpermittedParameters (found unpermitted parameters: a, b)
How above parameters should be permitted?
Here is what you need to do:
Remove Strong Parameters gem from your Gemfile.
Use this in the controller.
params.require(:my_key).permit({:one=>[:a, :b],:two=>[:c, :d]})
I think this has something to do with how nested attributes work. The ids "0", "1", "2" etc are implicit.
You can test in the console like this:
$ bin/rails c
Loading development environment (Rails 4.1.2)
2.1.0 :001 > params = ActionController::Parameters.new "my_key"=>{"one"=>{"0"=>{"a"=>"a_value","b"=>"b_value"},"1"=>{"a"=>"a_value","b"=>"b_value"},"2"=>{"a"=>"a_value","b"=>"b_value"}},"two"=>{"0"=>{"c"=>"c_value","d"=>"d_value"},"1"=>{"c"=>"c_value","d"=>"d_value"},"2"=>{"c"=>"c_value","d"=>"d_value"}}}
=> {"my_key"=>{"one"=>{"0"=>{"a"=>"a_value", "b"=>"b_value"}, "1"=>{"a"=>"a_value", "b"=>"b_value"}, "2"=>{"a"=>"a_value", "b"=>"b_value"}}, "two"=>{"0"=>{"c"=>"c_value", "d"=>"d_value"}, "1"=>{"c"=>"c_value", "d"=>"d_value"}, "2"=>{"c"=>"c_value", "d"=>"d_value"}}}}
2.1.0 :002 > p = params.require(:my_key).permit({:one=>[:a, :b],:two=>[:c, :d]})
=> {"one"=>{"0"=>{"a"=>"a_value", "b"=>"b_value"}, "1"=>{"a"=>"a_value", "b"=>"b_value"}, "2"=>{"a"=>"a_value", "b"=>"b_value"}}, "two"=>{"0"=>{"c"=>"c_value", "d"=>"d_value"}, "1"=>{"c"=>"c_value", "d"=>"d_value"}, "2"=>{"c"=>"c_value", "d"=>"d_value"}}}
2.1.0 :003 >
I would like to transform this
def some_process(k,v)
return "#{v}_#{k}"
end
a_hash = {
"i_love_hashes" => {
"thing" => 20,
"other_thing" => "0",
"yet_another_thing" => "i disagree",
"_peculiar_thing" => [
{"count" => 30,
"name" => "freddie"},
{"count" => 15,
"name" => "johhno"},
{"count" => 12,
"name" => "mohammed"},
]
},
"as_do_i" => {
"thing" => 10,
"other_thing" => "2",
"yet_another_thing" => "i strongly agree",
"_peculiar_thing" => [
{"count" => 10,
"name" => "frodo"},
{"count" => 4,
"name" => "bilbo"},
{"count" => 2,
"name" => "elizabeth"},
]
}
}
into this
{
"i_love_hashes"=>{
"thing"=>20,
"other_thing"=>"0",
"yet_another_thing"=>"i disagree",
"_peculiar_thing"=> [
{"count"=>30, "name"=>"freddie", :sinister_name=>"freddie_i_love_hashes"},
{"count"=>15, "name"=>"johhno", :sinister_name=>"johhno_i_love_hashes"},
{"count"=>12, "name"=>"mohammed", :sinister_name=>"mohammed_i_love_hashes"}
]},
"as_do_i"=>{
"thing"=>10,
"other_thing"=>"2",
"yet_another_thing"=>"i strongly agree",
"_peculiar_thing"=>[
{"count"=>10, "name"=>"frodo", :sinister_name=>"frodo_as_do_i"},
{"count"=>4, "name"=>"bilbo", :sinister_name=>"bilbo_as_do_i"},
{"count"=>2, "name"=>"elizabeth", :sinister_name=>"elizabeth_as_do_i"}
]
}
}
this is the code I am currently using to achieve this
a_hash.each_with_object({}) do |(k,v),o|
o.merge!({k =>
v.each_with_object({}) do |(a,b),g|
g.merge!({ a =>
(b.is_a?(Array) ? b.collect {|x| x.merge({sinister_name: (some_process k, x["name"])})} : b)
})
end
})
end
Ignoring the specific details of what is being returned by "some_process" (what is important is that it depends on the outer most key and the inner name values, in this example), are there any alternatives that would be considered more elegant?
Why not do a recursive function?
def add_siniter(hash)
hash[:siniter_name] = "#{hash['name']}_i_love_hashes"
hash
end
def format_hash(item)
case item
when Hash then item.keys.each{|key| format_hash(item[key])}
when Array then item.map!{|h| add_siniter(h)}
end
end
format_hash(a_hash)