我正在寻找一种避免在深度嵌套的哈希表中每个级别检查nil
的好方法。例如:
name = params[:company][:owner][:name] if params[:company] && params[:company][:owner] && params[:company][:owner][:name]
这需要进行三个检查,会导致代码非常丑陋。有没有什么方法可以避免这种情况?
我正在寻找一种避免在深度嵌套的哈希表中每个级别检查nil
的好方法。例如:
name = params[:company][:owner][:name] if params[:company] && params[:company][:owner] && params[:company][:owner][:name]
这需要进行三个检查,会导致代码非常丑陋。有没有什么方法可以避免这种情况?
Ruby 2.3.0版本在Hash
和Array
上引入了一种名为dig
的方法。
name = params.dig(:company, :owner, :name)
如果在任何层级上键值缺失,它将返回nil
。
如果你使用的是2.3版本以前的Ruby,你可以安装像ruby_dig
或hash_dig_and_collect
这样的gem,或者自己实现这个功能:
module RubyDig
def dig(key, *rest)
if value = (self[key] rescue nil)
if rest.empty?
value
elsif value.respond_to?(:dig)
value.dig(*rest)
end
end
end
end
if RUBY_VERSION < '2.3'
Array.send(:include, RubyDig)
Hash.send(:include, RubyDig)
end
params
是 nil
,那么 params.dig
将会失败。考虑使用安全导航操作符或与 .dig
结合使用,例如:params&.dig(:company, :owner, :name)
或 params&.company&.owner&.name
。 - thisismydesignparams&.[](:company)&.[](:owner)&.[](:name)
。 - thisismydesign在我看来,功能和清晰度之间的最佳折中方案是Raganwald的andand
。使用它,您可以这样做:
params[:company].andand[:owner].andand[:name]
这与try
相似,但在本例中读起来更好,因为您仍然像平常一样发送消息,但在调用引起注意的分隔符之间处理nils。
andand
在语法上很丑陋。 - mpdandand
根本没有意义(我理解它与&&
的引用)。我认为它的名称并没有恰当地传达它的含义。话虽如此,我还是比try
更喜欢它。 - mpd我不知道那是否是您所需要的,但也许您可以这样做?
name = params[:company][:owner][:name] rescue nil
params
是一个哈希表。使用rescue nil
仍然不可取。这里已经发布了更好、更简单的解决方案。没有理由去冒险尝试聪明地处理这个问题。 - thisismydesign更新:此答案已经过时。请使用当前被接受的答案所建议的dig
。
如果是Rails,请使用
params.try(:[], :company).try(:[], :owner).try(:[], :name)
哦,等等,那甚至更丑陋。;-)
这段文字的意思是:与用户mpd建议的第二个解决方案相同,只不过更符合Ruby语言的惯用方式。
class Hash
def deep_fetch *path
path.inject(self){|acc, e| acc[e] if acc}
end
end
hash = {a: {b: {c: 3, d: 4}}}
p hash.deep_fetch :a, :b, :c
#=> 3
p hash.deep_fetch :a, :b
#=> {:c=>3, :d=>4}
p hash.deep_fetch :a, :b, :e
#=> nil
p hash.deep_fetch :a, :b, :e, :f
#=> nil
您可能想了解一下将auto-vivification添加到Ruby哈希表中的方法之一。以下stackoverflow线程中提到了许多方法:
我会将此写为:
name = params[:company] && params[:company][:owner] && params[:company][:owner][:name]
虽然 Ruby 没有像 Io 中的 ? 运算符 那样简洁,但它仍然可以完成相同的功能。@ThiagoSilveira 给出的答案也不错,尽管可能会慢一些。
做:
params.fetch('company', {}).fetch('owner', {})['name']
NilClass
中适当的方法来避免nil
,如果它是数组、字符串或数字的话。只需将to_hash
添加到此列表的清单中并使用即可。class NilClass; def to_hash; {} end end
params['company'].to_hash['owner'].to_hash['name']
你能避免使用多维哈希表,而使用
params[[:company, :owner, :name]]
或者
params[[:company, :owner, :name]] if params.has_key?([:company, :owner, :name])
instead?
?
运算符。实际上,我对等效运算符很感兴趣。您仍然可以扩展哈希类并添加该运算符。 - Pasta