Slicing Params Hash for Specific Values

Slicing params hash for specific values

I changed by mind. The previous one doesn't seem to be any good.

class Hash
def slice1(*keys)
keys.each_with_object({}){|k, h| h[k] = self[k]}
end
def slice2(*keys)
h = {}
keys.each{|k| h[k] = self[k]}
h
end
end

Editing params nested hash

still keep the output as a params hash (still containing nested hashes arrays

Sure.

You'll have to manipulate the params hash, which is done in the controller.

Whilst I don't have lots of experience with this I just spent a bunch of time testing -- you can use a blend of the ActionController::Parameters class and then using gsub! -- like this:

#app/controllers/your_controller.rb
class YourController < ApplicationController
before_action :set_params, only: :create

def create
# Params are passed from the browser request
@model = Model.new params_hash
end

private

def params_hash
params.require(:x).permit(:y).each do |k,v|
v.gsub!(/[regex]/, 'string')
end
end
end

I tested this on one of our test apps, and it worked perfectly:

Sample Image

Sample Image

Sample Image

--

There are several important points.

Firstly, when you call a strong_params hash, params.permit creates a new hash out of the passed params. This means you can't just modify the passed params with params[:description] = etc. You have to do it to the permitted params.

Secondly, I could only get the .each block working with a bang-operator (gsub!), as this changes the value directly. I'd have to spend more time to work out how to do more elaborate changes.

--

Update

If you wanted to include nested hashes, you'd have to call another loop:

def params_hash
params.require(:x).permit(:y).each do |k,v|
if /_attributes/ ~= k
k.each do |deep_k, deep_v|
deep_v.gsub!(/[regex]/, 'string'
end
else
v.gsub!(/[regex]/, 'string')
end
end
end

More-efficient way to pass the Rails params hash to named route

Refactored the helper function:

def options(hash)
o={:employer=>params[:employer],:location=>params[:location],:posted=>params[:posted],:starting=>params[:starting],:sort=>params[:sort],:dir=>params[:dir],:fav=>params[:fav]}
# add the contents of hash, overwriting entries with duplicate keys
o.merge!(hash)
end

Refactored the view code to pass hash instead of key/value pair; greater flexibility:

<%= link_to_unless params[:employer]==employer, employer, jobs_path( options({:employer=>employer}) ) %>

and

<%= link_to_unless_current '✗', jobs_path( options({:employer=>nil}) ) %>

Rails: except vs slice to take out certain parameter?

Using except with parameter hashes is dangerous because you're saying "allow any parameter other than this one" so it opens your model up to mass-assignment attacks.

Unless I'm not thinking of another attack vector you should be fine, because you're filtering your acceptable parameter hash with strong_parameters in your whiteList_function. So, you're fine further filtering that hash with either slice or except, whichever is more appealing to you.

Rails 5: unable to retrieve hash values from parameter

take a look to this. Very weird since ActionController::Parameters is a subclass of Hash, you can convert it directly to a hash using the to_h method on the params hash.

However to_h only will work with whitelisted params, so you can do something like:

permitted = params.require(:line_item).permit(: line_item_attributes_attributes)
attributes = permitted.to_h || {}
attributes.values

But if instead you do not want to whitelist then you just need to use the to_unsafe_h method.

Update

I was very curious about this issue, so I started researching, and now that you clarified that you are using Rails 5, well that's the cause of this issue, as @tillmo said in stable releases of Rails like 4.x, ActionController::Parameters is a subclass of Hash, so it should indeed respond to the values method, however in Rails 5 ActionController::Parameters now returns an Object instead of a Hash

Note: this doesn’t affect accessing the keys in the params hash like params[:id]. You can view the Pull Request that implemented this change.

To access the parameters in the object you can add to_h to the parameters:

params.to_h

If we look at the to_h method in ActionController::Parameters we can see it checks if the parameters are permitted before converting them to a hash.

# actionpack/lib/action_controller/metal/strong_parameters.rb
def to_h
if permitted?
@parameters.to_h
else
slice(*self.class.always_permitted_parameters).permit!.to_h
end
end

for example:

def do_something_with_params
params.slice(:param_1, :param_2)
end

Which would return:

{ :param_1 => "a", :param_2 => "2" }

But now that will return an ActionController::Parameters object.

Calling to_h on this would return an empty hash because param_1 and param_2 aren’t permitted.

To get access to the params from ActionController::Parameters, you need to first permit the params and then call to_h on the object

def do_something_with_params
params.permit([:param_1, :param_2]).to_h
end

The above would return a hash with the params you just permitted, but if you do not want to permit the params and want to skip that step there is another way using to_unsafe_hash method:

def do_something_with_params
params.to_unsafe_h.slice(:param_1, :param_2)
end

There is a way of always permit the params from a configuration from application.rb, if you want to always allow certain parameters you can set a configuration option. Note: this will return the hash with string keys, not symbol keys.

#controller and action are parameters that are always permitter by default, but you need to add it in this config.
config.always_permitted_parameters = %w( controller action param_1 param_2)

Now you can access the params like:

def do_something_with_params
params.slice("param_1", "param_2").to_h
end

Note that now the keys are strings and not symbols.

Hope this helps you to understand the root of your issue.

Source: eileen.codes

Ruby: Easiest Way to Filter Hash Keys?

Edit to original answer: Even though this is answer (as of the time of this comment) is the selected answer, the original version of this answer is outdated.

I'm adding an update here to help others avoid getting sidetracked by this answer like I did.

As the other answer mentions, Ruby >= 2.5 added the Hash#slice method which was previously only available in Rails.

Example:

> { one: 1, two: 2, three: 3 }.slice(:one, :two)
=> {:one=>1, :two=>2}

End of edit. What follows is the original answer which I guess will be useful if you're on Ruby < 2.5 without Rails, although I imagine that case is pretty uncommon at this point.


If you're using Ruby, you can use the select method. You'll need to convert the key from a Symbol to a String to do the regexp match. This will give you a new Hash with just the choices in it.

choices = params.select { |key, value| key.to_s.match(/^choice\d+/) }

or you can use delete_if and modify the existing Hash e.g.

params.delete_if { |key, value| !key.to_s.match(/choice\d+/) }

or if it is just the keys and not the values you want then you can do:

params.keys.select { |key| key.to_s.match(/^choice\d+/) }

and this will give the just an Array of the keys e.g. [:choice1, :choice2, :choice3]

Is there some method or an easy way so that I can return a hash that includes only the given keys?

I think what you are looking for is slice, which will return a hash with only the given keys.

How Can I extract the compete hash of parameters sent along with controler call in rails?

Rails parameters are instances of HashWithIndifferentAccess, a subclass of Hash.

params.class
# => HashWithIndifferentAccess
params.is_a? Hash
# => true

You can get the entire Hash with params:

params
# => {"id"=>"aom7v66e309yjkd2x0aq", "video_type"=>"trailer"}

Or with #to_hash if you need a plain Hash:

params.to_hash
# => {"id"=>"aom7v66e309yjkd2x0aq", "video_type"=>"trailer"}
params.to_hash.class
# => Hash

The keys with #keys:

params.keys
# => ["id", "video_type"]

And the values with #values:

params.values
# => ["aom7v66e309yjkd2x0aq", "trailer"]

To extract specific keys, you can use #slice:

params = {"a"=>1, "b"=>2, "c"=>3}
params.slice("a", "c")
# => {"a"=>1, "c"=>3}

Or its counterpart #except:

params = {"a"=>1, "b"=>2, "c"=>3}
params.except("b")
# => {"a"=>1, "c"=>3}


Related Topics



Leave a reply



Submit