【问题标题】:How to generate direct access keys to nested hash which contains hash and arrays as values?如何生成包含哈希和数组作为值的嵌套哈希的直接访问键?
【发布时间】:2015-07-27 10:34:44
【问题描述】:

我想比较两个 XML 文件,其中一个是输入,另一个是输出。我将两者都转换为哈希。

我的想法是将输入 XML 中的所有键转换为哈希,并在输入和输出哈希中搜索每个键以查找它们各自的键/值对。

我有一个哈希:

{
  "requisition_header" => {
    "requested_by" => {"login" => "coupasupport"},
    "department" => {"name" => "Marketing"},
    "ship_to_address" => {"name" => "Address_1431693296"},
    "justification" => nil,
    "attachments" => [],
    "requisition_lines" => [
      {
        "description" => "Cleaning Services for Building A",
        "line_num" => 1,
        "need_by_date" => 2010-09-23 07:00:00 UTC,
        "source_part_num" => nil,
        "supp_aux_part_num" => nil,
        "unit_price" => #<BigDecimal:a60520c,'0.3E4',9(18)>,
        "supplier" => {"name" => "amazon.com"},
          "account" => {
          "code" => "SF-Marketing-Indirect",
          "account_type" => {"name" => "Ace Corporate"}
        },
        "currency" => {"code" => "USD"},
        "payment_term" => {"code" => "Net 30"},
        "shipping_term" => {"code" => "Standard"},
        "commodity" => {"name" => "Marketing-Services"}
      }
    ]
  }
}

它是嵌套的,所有的值都不能直接访问。

我想要一种方法来生成对散列中每个值的直接访问。

例如:

requisition_header.requested_by.login

将访问“coupasupport”。

requisition_header.department.name

将访问“营销”。

requisition_header.requisition_lines[0].description

将访问“A 楼清洁服务”。

requisition_header.requisition_lines[0].line_num

将访问“1”。

requisition_header.requisition_lines[0].need_by_date

将访问“2010-09-23 07:00:00 UTC”。

构建的每个键都可用于直接在散列中搜索值。

【问题讨论】:

  • 您的示例哈希太大。在提供样本数据时,将其减少到证明问题所需的最低限度。再长一点,你就会浪费我们的时间,耽误我们帮助你的能力。

标签: ruby


【解决方案1】:

你可以使用BasicObject#method_missing:

代码

class Hash
  def method_missing(key,*args)
    (args.empty? && key?(key)) ? self[key] : super
  end
end

示例

hash = { animals: {
           pets: { dog: "Diva", cat: "Boots", python: "Stretch" },
           farm: { pig: "Porky", chicken: "Little", sheep: "Baa" }
         },
         finishes: {
           tinted: { stain: "Millers", paint: "Oxford" },  
           clear:  { lacquer: "Target", varnish: "Topcoat" }
         }
       }

hash.finishes.tinted.stain
  #=> "Millers
hash.animals.pets.cat
  #=> "Boots"
hash.animals.pets
  #=> {:dog=>"Diva", :cat=>"Boots", :python=>"Stretch"}
hash.animals
  #=> {:pets=>{:dog=>"Diva", :cat=>"Boots", :python=>"Stretch"},
  #    :farm=>{:pig=>"Porky", :chicken=>"Little", :sheep=>"Baa"}} 

读者挑战

这种方法有一个潜在的“陷阱”。我把它留给读者来识别它。我的示例包含一个线索。 (请注意,可能还有其他我没有想到的问题。)

【讨论】:

    【解决方案2】:

    您也可以通过覆盖 OpenStruct#new 来做到这一点,

    require 'ostruct'
    
    class DeepStruct < OpenStruct
      def initialize(hash=nil)
        @table = {}
        @hash_table = {}
    
        if hash
          hash.each do |k,v|
            @table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
            @hash_table[k.to_sym] = v
    
            new_ostruct_member(k)
          end
        end
      end
    
      def to_h
        @hash_table
      end
    
    end
    

    现在你可以这样做了:

    require 'deep_struct'
    
    hash = {"requisition_header"=>{"requested_by"=>{"login"=>"coupasupport"}, "department"=>{"name"=>"Marketing"}, "ship_to_address"=>{"name"=>"Address_1431693296"}, "justification"=>nil, "attachments"=>[], "requisition_lines"=>[{"description"=>"Cleaning Services for Building A", "line_num"=>1, "need_by_date"=>2010-09-23 07:00:00 UTC, "source_part_num"=>nil, "supp_aux_part_num"=>nil, "unit_price"=>#<BigDecimal:a60520c,'0.3E4',9(18)>, "supplier"=>{"name"=>"amazon.com"}, "account"=>{"code"=>"SF-Marketing-Indirect", "account_type"=>{"name"=>"Ace Corporate"}}, "currency"=>{"code"=>"USD"}, "payment_term"=>{"code"=>"Net 30"}, "shipping_term"=>{"code"=>"Standard"}, "commodity"=>{"name"=>"Marketing-Services"}}]}}
    
    mystruct = DeepStruct.new hash
    mystruct.requisition_header.requested_by.login # => coupasupport
    mystruct.requisition_header.to_h # => {"requested_by"=>{"login"=>"coupasupport"}
    

    【讨论】:

    • 似乎,我不清楚这个问题..mystruct.requisition_header.requested_by.login 会提供 coupasupport,但我的目标是生成这些密钥并将它们存储在一个数组中。我将使用这些键数组在不同的哈希中搜索。我将使用github.com/chengguangnan/vine 搜索不同哈希中的键
    • 我仍然不清楚您的期望,您能否更新您的问题并举例说明?
    • 嗨 Sharvy Ahmed。我期待一种算法,该算法采用与哈希和数组深度嵌套的哈希作为其中的值,并为哈希中的每个值生成直接访问.. 例如:a = {b: {c: d:{ e: 'f'}}} 那么值 f 可以通过键 bcde 访问
    【解决方案3】:

    这可以通过以下方法完成,将嵌套的哈希转换为嵌套的OpenStructs:

    require 'ostruct'
    def deep_structify(hash)
      result = {}
      hash.each do |key, value|
        result[key] = value.is_a?(Hash) ? deep_structify(value) : value
      end if hash
      OpenStruct.new(result)
    end
    
    hash = {"requisition_header"=>{"requested_by"=>{"login"=>"coupasupport"}, "department"=>{"name"=>"Marketing"}, "ship_to_address"=>{"name"=>"Address_1431693296"}, "justification"=>nil, "attachments"=>[], "requisition_lines"=>[{"description"=>"Cleaning Services for Building A", "line_num"=>1, "need_by_date"=>2010-09-23 07:00:00 UTC, "source_part_num"=>nil, "supp_aux_part_num"=>nil, "unit_price"=>#<BigDecimal:a60520c,'0.3E4',9(18)>, "supplier"=>{"name"=>"amazon.com"}, "account"=>{"code"=>"SF-Marketing-Indirect", "account_type"=>{"name"=>"Ace Corporate"}}, "currency"=>{"code"=>"USD"}, "payment_term"=>{"code"=>"Net 30"}, "shipping_term"=>{"code"=>"Standard"}, "commodity"=>{"name"=>"Marketing-Services"}}]}}
    
    struct = deep_structify(hash)
    
    struct.requisition_header.department.name
    #=> "Marketing"
    

    【讨论】:

    猜你喜欢
    • 2013-04-30
    • 2013-03-22
    • 2020-07-07
    • 2020-07-02
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    相关资源
    最近更新 更多