我有兴趣获取params哈希的嵌套'name'参数。调用类似
的内容params[:subject][:name]
当params [:subject]为空时,会抛出错误。为了避免这个错误,我通常写这样的东西:
if params[:subject] && params[:subject][:name]
有更简洁的方法来实现这个吗?
答案 0 :(得分:19)
答案 1 :(得分:17)
您可以使用#try
,但我认为这不会更好:
params[:subject].try(:[], :name)
或者使用#fetch
默认参数:
params.fetch(:subject, {}).fetch(:name, nil)
或者您可以将#default=
设置为新的空哈希,但不要尝试修改从此返回的值:
params.default = {}
params[:subject][:name]
它也打破了所有存在的简单测试,所以你不能写:
if params[:subject]
因为它将返回空哈希,现在你必须为每个测试添加#present?
调用。
此外,当key没有值时,它总会返回哈希,即使你期望字符串也是如此。
但是从我看到的,你尝试提取嵌套参数,而不是将其分配给模型并放置你的逻辑。如果你有Subject
模型,那么只需指定:
@subject = Subject.new(params[:subject])
shuld提取用户填写的所有参数。然后,您尝试保存它们,以查看用户是否传递了有效值。
如果您担心访问用户不应设置的字段,请为允许使用质量分配设置的字段添加attr_accessible
白名单(如我的示例所示,使用@subject.attributes = params[:subject]
更新)
答案 2 :(得分:9)
Ruby 2.3.0使#dig
更容易实现h = {foo: {bar: {baz: 1}}}
h.dig(:foo, :bar, :baz) #=> 1
h.dig(:foo, :zot, :baz) #=> nil
答案 3 :(得分:5)
params[:subject].try(:[], :name)
是最干净的方式
答案 4 :(得分:4)
当我在编码时遇到同样的问题时,我有时会使用`rescue'。
name = params[:subject][:name] rescue ""
# => ""
这不礼貌,但我觉得这很简单。
编辑:我不再经常使用这种方式了。我建议try
或fetch
。
答案 5 :(得分:2)
不是真的。您可以尝试fetch
或try
(来自ActiveSupport),但它并不比您现有的更干净。
更多信息:
更新:忘记了andand
:
andand
可以让您:
params[:user].andand[:name] # nil guard is built-in
同样,您可以使用Ick library per the answer above中的maybe
。
答案 6 :(得分:2)
或者,向其添加[]
。
class NilClass; def [](*); nil end end
params[:subject][:name]
答案 7 :(得分:1)
class Hash
def fetch2(*keys)
keys.inject(self) do |hash, key|
hash.fetch(key, Hash.new)
end
end
end
e.g。
require 'minitest/autorun'
describe Hash do
it "#fetch2" do
{ yo: :lo }.fetch2(:yo).must_equal :lo
{ yo: { lo: :mo } }.fetch2(:yo, :lo).must_equal :mo
end
end
答案 8 :(得分:1)
我从这里的答案中透露了这个:
How to check if params[:some][:field] is nil?
我一直在寻找更好的解决方案。
所以我想让我们用try
以不同的方式测试所设置的嵌套键:
params[:some].try(:has_key?, :field)
这还不错。如果未设置,则会nil
与false
进行比较。如果参数设置为true
,您也会获得nil
。
答案 9 :(得分:0)
我为这个用例写了Dottie - 深入到哈希,而不先知道整个预期的树是否存在。语法比使用try
(Rails)或maybe
(Ick)更简洁。例如:
# in a Rails request, assuming `params` contains:
{ 'person' => { 'email' => 'jon@example.com' } } # there is no 'subject'
# standard hash access (symbols will work here
# because params is a HashWithIndifferentAccess)
params[:person][:email] # => 'jon@example.com'
params[:subject][:name] # undefined method `[]' for nil:NilClass
# with Dottie
Dottie(params)['person.email'] # => 'jon@example.com'
Dottie(params)['subject.name'] # => nil
# with Dottie's optional class extensions loaded, this is even easier
dp = params.dottie
dp['person.email'] # => 'jon@example.com'
dp['subject.name'] # => nil
dp['some.other.deeply.nested.key'] # => nil
如果您想了解更多信息,请查看文档:{{3}}
答案 10 :(得分:0)
我用过:
params = {:subject => {:name => "Jack", :actions => {:peaceful => "use internet"}}}
def extract_params(params, param_chain)
param_chain.inject(params){|r,e| r=((r.class.ancestors.include?(Hash)) ? r[e] : nil)}
end
extract_params(params, [:subject,:name])
extract_params(params, [:subject,:actions,:peaceful])
extract_params(params, [:subject,:actions,:foo,:bar,:baz,:qux])
给出:
=> "Jack"
=> "use internet"
=> nil
答案 11 :(得分:0)
您可以使用内联分配来避免双重哈希访问:
my_param = subj_params = params[:subject] && subj_params[:name]