Ruby Style:如何检查嵌套的哈希元素是否存在

Tod*_*d R 56 ruby hash coding-style

考虑存储在哈希中的"人".两个例子是:

fred = {:person => {:name => "Fred", :spouse => "Wilma", :children => {:child => {:name => "Pebbles"}}}}
slate = {:person => {:name => "Mr. Slate", :spouse => "Mrs. Slate"}} 
Run Code Online (Sandbox Code Playgroud)

如果"人"没有任何子女,则"儿童"元素不存在.所以,对于Slate先生,我们可以检查他是否有父母:

slate_has_children = !slate[:person][:children].nil?
Run Code Online (Sandbox Code Playgroud)

那么,如果我们不知道"slate"是一个"人"哈希呢?考虑:

dino = {:pet => {:name => "Dino"}}
Run Code Online (Sandbox Code Playgroud)

我们不能再轻易检查孩子了:

dino_has_children = !dino[:person][:children].nil?
NoMethodError: undefined method `[]' for nil:NilClass
Run Code Online (Sandbox Code Playgroud)

那么,你如何检查哈希的结构,特别是如果它被深深嵌套(甚至比这里提供的例子更深)?也许更好的问题是:这样做的"红宝石方式"是什么?

tad*_*man 86

最明显的方法是简单地检查每一步:

has_children = slate[:person] && slate[:person][:children]
Run Code Online (Sandbox Code Playgroud)

使用.nil?实际上只有在使用false作为占位符值时才需要,实际上这种情况很少见.通常你可以简单地测试它是否存在.

更新:如果您使用的是Ruby 2.3或更高版本,则会有一个内置dig方法,可以执行此答案中描述的内容.

如果没有,您还可以定义自己的哈希"挖掘"方法,这可以大大简化:

class Hash
  def dig(*path)
    path.inject(self) do |location, key|
      location.respond_to?(:keys) ? location[key] : nil
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

此方法将检查方法的每个步骤,并避免绊倒对nil的调用.对于浅层结构,实用程序有些限制,但对于深度嵌套的结构,我发现它非常有用:

has_children = slate.dig(:person, :children)
Run Code Online (Sandbox Code Playgroud)

您也可以使其更加健壮,例如,测试:children条目是否实际填充:

children = slate.dig(:person, :children)
has_children = children && !children.empty?
Run Code Online (Sandbox Code Playgroud)

  • @tadman fyi通过这个SO答案,#dig现在在Ruby主干:https://www.ruby-lang.org/en/news/2015/11/11/ruby-2-3-0-preview1-released/ :d (3认同)
  • is_a?(哈希) - 好主意.保持简洁简洁:p (2认同)

Mar*_*rez 21

使用Ruby 2.3,我们将支持安全导航操作员:https: //www.ruby-lang.org/en/news/2015/11/11/ruby-2-3-0-preview1-released/

has_children 现在可以写成:

has_children = slate[:person]&.[](:children)
Run Code Online (Sandbox Code Playgroud)

dig 正在添加:

has_children = slate.dig(:person, :children)
Run Code Online (Sandbox Code Playgroud)

  • 如果您使用Ruby <2.3,我刚刚发布了一个gem,它添加了2.3兼容的Hash#dig和array#dig方法:https://rubygems.org/gems/ruby_dig (6认同)

Cam*_*tin 13

另一种选择:

dino.fetch(:person, {})[:children]
Run Code Online (Sandbox Code Playgroud)