有没有一种干净的方法可以避免在嵌套的参数哈希中调用 nil 上的方法? [复制]

Posted

技术标签:

【中文标题】有没有一种干净的方法可以避免在嵌套的参数哈希中调用 nil 上的方法? [复制]【英文标题】:Is there a clean way to avoid calling a method on nil in a nested params hash? [duplicate] 【发布时间】:2011-07-22 17:38:57 【问题描述】:

我对获取 params 哈希的嵌套“名称”参数感兴趣。调用类似的东西

params[:subject][:name]

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

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

有没有更简洁的方法来实现这个?

【问题讨论】:

几天前也是同样的问题 :) ***.com/questions/5393974/… 【参考方案1】:

Ruby 2.3.0 让 #dig 可以轻松实现这一点

h = foo: bar: baz: 1

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

【讨论】:

如果hnilh.dig 将失败。考虑改用安全导航运算符或与.dig 结合使用:h&.dig(:foo, :bar, :baz)h&.foo&.bar&.baz 我之前的评论中安全导航运算符的哈希值语法不正确。正确的语法是:h&.[](:foo)&.[](:bar)&.[](:baz)【参考方案2】:

检查Ick 的也许。您不需要大量重构代码,只需在必要时散布可能代理:

params[:subject].maybe[:name]

同一作者(raganwald)也写了andand,想法相同。

【讨论】:

有趣,我以前没听说过 Ick。 现在 ruby​​forge 消失了,上面指向 Ick 的可能插件的链接已损坏。新的 gems 站点 ruby​​gems.org 有一个 gem,可能在这里:rubygems.org/gems/maybe。这和来自 ruby​​ forge 的 Ick 是同一个吗? @steel 的最佳答案如下:使用 Ruby 2.3.0 中引入的名为 dig 的内置功能。 【参考方案3】:

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

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

【讨论】:

【参考方案4】:

我在这里的回答中交叉发布了这个:

How to check if params[:some][:field] is nil?

我也一直在寻找更好的解决方案。

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

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

还不错。如果未设置,您将得到 nilfalse。如果参数设置为nil,您还将获得true

【讨论】:

【参考方案5】:
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

【讨论】:

【参考方案6】:

当我在编码中遇到同样的问题时,我有时会使用 `rescue'。

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

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

编辑:我不再经常使用这种方式了。我推荐tryfetch

【讨论】:

【参考方案7】:

我用过:

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

【讨论】:

【参考方案8】:

我为这个用例写了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

如果您想了解更多信息,请查看文档:https://github.com/nickpearson/dottie

【讨论】:

【参考方案9】:

    你可以用#try,但我觉得也好不了多少:

    params[:subject].try(:[], :name)
    

    或者使用#fetch带默认参数:

    params.fetch(:subject, ).fetch(:name, nil)
    

    或者您可以将#default= 设置为新的空哈希,但不要尝试修改由此返回的值:

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

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

    if params[:subject]
    

    因为它会返回空哈希,现在您必须在每个测试中添加#present? 调用。

    此外,当键没有值时,这总是返回哈希,即使您期望字符串也是如此。

但据我所见,您尝试提取嵌套参数,而不是将其分配给模型并在那里放置您的逻辑。如果您有Subject 模型,那么只需分配:

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

应该提取用户填写的所有参数。然后你尝试保存它们,看看用户是否传递了有效的值。

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

【讨论】:

【参考方案10】:

或者,添加[]

class NilClass; def [](*); nil end end
params[:subject][:name]

【讨论】:

这很棒。我删除了我的,因为这个比我的好得多。并且,感谢您展示了使用不带名称的 splat 运算符 *。我不知道,从你那里学来的。 可以让更多人评论吗?这对我来说似乎很棒!【参考方案11】:

不是真的。您可以尝试 fetchtry(来自 ActiveSupport),但它并不比您已有的干净。

更多信息在这里:

Nested hash defined?()

更新:忘记andand

http://andand.rubyforge.org/

andand 让你这样做:

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

同样,您可以使用maybe 中的Ick library per the answer above。

【讨论】:

【参考方案12】:

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

【讨论】:

但是如果主题哈希中没有 :name 字段,则会引发异常。您必须将默认值添加到 #fetch 啊抱歉我的意思是params[:subject].try(:[], :name),我同意你的观点,不一定比基本的详细方式更好。 在 railscasts 中,我认为 Ryan 在没有 :[] 的情况下使用 try(从那时起我就一直在使用它)。这将使该解决方案更加紧凑;) :[] 是纯红宝石。这就是您将 [] 类型方法表示为符号的方式。

以上是关于有没有一种干净的方法可以避免在嵌套的参数哈希中调用 nil 上的方法? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:从嵌套视图调用方法

是否有将上下文数据传递给@Asynchronous ejb调用的干净方法?

传递哈希而不是方法参数[关闭]

有没有一种方法可以将宏名称作为参数传递给嵌套宏,而不会在扩展最外层的宏时扩展它们?

有没有一种干净的方法可以在 Python 中编写多行字符串? [复制]

干净的代码和嵌套的承诺 [重复]