有没有一个干净的方法,以避免调用一个嵌套的参数哈希零的方法?

我有兴趣获取params散列的嵌套的“名称”参数。 调用类似

params[:subject][:name] 

当params [:subject]为空时抛出一个错误。 为了避免这个错误,我通常会写这样的东西:

 if params[:subject] && params[:subject][:name] 

有没有一个更清晰的方式来实现这一点?

检查伊克 也许 。 你不需要重构你的代码,只需要在必要时散布代理即可:

 params[:subject].maybe[:name] 

同一个作者( raganwald )也写了和有同样的想法。

  1. 你可以使用#try ,但我不认为它好多了:

     params[:subject].try(:[], :name) 
  2. 或者使用默认参数#fetch

     params.fetch(:subject, {}).fetch(:name, nil) 
  3. 或者,您可以将#default=设置为新的空散列,但不要尝试修改从此返回的值:

     params.default = {} params[:subject][:name] 

    它也打破了存在的所有简单testing,所以你不能写:

     if params[:subject] 

    因为它会返回空的散列,现在你必须添加#present? 呼吁每一个testing。

    当key没有值的时候,这总是返回散列值,即使你期望string。

但是从我所看到的,你尝试提取嵌套的参数,而不是分配给模型,并在那里放置你的逻辑。 如果您有Subject模型,那么只需指定:

 @subject = Subject.new(params[:subject]) 

shuld提取所有用户填写的参数表单。 然后尝试保存它们,以查看用户是否传递了有效值。

如果您担心访问用户不应该设置的字段,那么添加attr_accessible白名单,以便允许用质量分配来设置的字段(在我的示例中,用@subject.attributes = params[:subject]进行更新)

Ruby 2.3.0使用#dig很容易做到这一点

 h = {foo: {bar: {baz: 1}}} h.dig(:foo, :bar, :baz) #=> 1 h.dig(:foo, :zot, :baz) #=> nil 

params[:subject].try(:[], :name)最干净的方法

当我在编码方面有同样的问题时,我有时会使用“救援”。

 name = params[:subject][:name] rescue "" # => "" 

这不是礼貌,但我认为这是简单的方法。

编辑:我不经常使用这种方式了。 我build议tryfetch

不是真的。 您可以尝试(从ActiveSupport) fetchtry ,但它不是比你已经有的更干净。

更多信息:

  • 嵌套散列定义?()

更新:忘记和和:

andand让你做:

 params[:user].andand[:name] # nil guard is built-in 

同样,你可以使用上面的答案,从Ick库 。

或者,向其中添加[]

 class NilClass; def [](*); nil end end params[:subject][:name] 
 class Hash def fetch2(*keys) keys.inject(self) do |hash, key| hash.fetch(key, Hash.new) end end end 

例如

 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 

我交叉从这里回答这个:

如何检查params [:some] [:field]是否为零?

我一直在寻找更好的解决scheme。

所以我想我们使用try一种不同的方式来testing被设置的嵌套键:

 params[:some].try(:has_key?, :field) 

不算太差。 如果没有设置,你会得到nilfalse 。 如果param被设置nil你也会变成true

我为这个用例写了Dottie – 深入到一个散列,而不知道整个预期的树是否存在。 语法比使用try (Rails)或者(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 

查看文档,如果你想看到更多: https : //github.com/nickpearson/dottie

我用了:

 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 

您可以通过内联分配避免双重哈希访问:

 my_param = subj_params = params[:subject] && subj_params[:name]