【问题标题】:Iterating through a ruby nested hash with nils? [duplicate]使用 nils 遍历 ruby​​ 嵌套哈希? [复制]
【发布时间】:2011-12-12 19:07:37
【问题描述】:

假设我正在从 API 取回 JSON 嵌套哈希(或哈希数组)

@example = {"results" = > {{"poop" => "shoop"},{"foo" => {"shizz" => "fizz", "nizzle"=>"bizzle"}}}

上面嵌套哈希的 YAML 标记

  - poop: shoop
  - foo:
    shizz: fizz
    nizzle: bizzle

现在让我们从散列中使用 ActiveRecord 创建一个数据库条目。这很好用。

Thing.create!(:poop  => @example["results"]["poop"],
                :shizz => @example["results"]["foo"]["shizz"],
                :nizzle=> @example["results"]["foo"]["nizzle"])

但如果 'foo' 为空或 nil 怎么办?例如,如果 API 结果有一个带有 "first name","last name" # 等的 "person" 哈希,则 "person " 如果没有数据,hash 通常会为空,也就是说里面的 hash 不存在。

@example = {"results" = > {{"poop" => "shoop"},{"foo" => nil }}

  Thing.create!(:poop  => @example["results"]["poop"],
                :shizz => @example["results"]["foo"]["shizz"],
                :nizzle=> @example["results"]["foo"]["nizzle"])

  NoMethodError: You have a nil object when you didn't expect it! 
  You might have expected an instance of Array. 
  The error occurred while evaluating nil.[]

处理此问题的最佳方法是什么?

【问题讨论】:

    标签: ruby hash error-handling hash-of-hashes


    【解决方案1】:

    不久前我遇到了nil 敏感的Hash#get 方法。

    class Hash
      def get(key, default=nil)
        key.split(".").inject(self){|memo, key_part| memo[key_part] if memo.is_a?(Hash)} || default
      end
    end
    
    h = { 'a' => { 'b' => { 'c' => 1 }}}
    puts h.get "a.b.c"    #=> 1
    puts h.get "a.b.c.d"  #=> nil
    puts h.get "not.here" #=> nil
    

    这种 JSON 钻探非常方便。

    否则你必须这样做:

    h['a'] && h['a']['b'] && h['a']['b']['c']
    

    这简直糟透了。

    【讨论】:

    • 确实很有趣,如何“耗尽”备忘录哈希
    • slick - 会派上用场的
    • 那么我会创建一个扩展系统“Hash”类的自定义类,还是将这个“get”方法修改为“Hash”?
    • 扩展标准库提供的类很少会造成重大损害。我不会让 gem 依赖于这种方法或任何东西,但它可以在您自己的应用程序中使用。因此,只需将其滚动到 Hash 并享受 {'a' => 1}.get 'a' 的乐趣
    【解决方案2】:

    Ruby 2.3.0 在HashArray 上都引入了a new method called dig,完全解决了这个问题。

    value = hash.dig(:a, :b)
    

    如果密钥在任何级别丢失,则返回nil

    【讨论】:

      【解决方案3】:

      如果您使用的是 rails(不确定它是否在 ruby​​ 1.9 中):

      h = {"a"=>1}
      h.try(:[],"a") #1
      h.try(:[],"b") #nil
      
      h2 = {"c"=>{"d"=>1}}
      h2.try(:[],"c").try(:[],"d")   #1
      h2.try(:[],"a").try(:[],"foo") #nil
      
      
      # File activesupport/lib/active_support/core_ext/object/try.rb, line 28
      def try(*a, &b)
        if a.empty? && block_given?
          yield self
        else
          __send__(*a, &b)
        end
      end
      
      

      【讨论】:

      • 我喜欢这个用于 Rails 应用程序的解决方案,但我正在寻找更底层的纯 Ruby 解决方案。我会试试这个,谢谢!
      【解决方案4】:

      我继续并开始将所有哈希结果传递给Hashie Mash。这样它们的行为就像 Ruby 对象,并且像冠军一样响应 nil!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-04-03
        • 1970-01-01
        • 2022-12-11
        • 2013-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多