【问题标题】:Ruby: initialize a Ruby Class with a nested Hash and some pre-defined default valuesRuby:使用嵌套哈希和一些预定义的默认值初始化 Ruby 类
【发布时间】:2016-11-24 06:38:22
【问题描述】:

这是我的问题。我喜欢Andrea Pavoni 允许使用嵌套哈希来初始化类的方式。

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

但我找不到一种方法来包含具有特定默认值的哈希(在类中),因此行为如下:

没有默认的原始行为(上面的代码):

input_hash = {a: {b: 1}}
new_object = DeepStruct.new hash
new_object.a      # => #<DeepStruct b=1>
new_object.a.b    # => 1
new_object.a.to_h # => {b: 1}

在类中定义了以下 default_h:

default_h = {a: {dc: 2}, dd: {de: 4}}

input_hash 和 default_h 应该合并如下 (实际上使用deep_merge 进行嵌套哈希)

{:a=>{:dc=>2, :b=>1}, :dd=>{:de=>4}}

默认哈希的行为应该是:

new_object = DeepStruct.new hash
new_object.a.b    # => 1
new_object.a.dc   # => 2
new_object.a.to_h # => {:dc=>2, :b=>1}

我找不到在类中实现此行为的方法。我非常感谢您在这件事上的任何帮助。

编辑:现在尝试在类中使用 David 的代码:

class CompMedia
    require 'ostruct'
    attr_accessor :merged_h

    def initialize(hash)
        defaults = {a: {dc: 2}, dd: {de: 4}}
      @merged_h = {}
      deep_update(merged_h, defaults)
      deep_update(merged_h, hash)
      @merged_h
    end

    def deep_update(dest, src)
      src.each do |key, value|
        if value.is_a?(Hash)
          dest[key] = {} if !dest[key].is_a?(Hash)
          deep_update(dest[key], value)
        else
          dest[key] = value
        end
      end
    end

    def deep_open_struct(hash)
      result = OpenStruct.new
      hash.each do |key, value|
        if value.is_a?(Hash)
          result[key] = deep_open_struct(value)
        else
          result[key] = value
        end
      end
        result
    end

end # class CompMedia

input_hash = {a: {b: 1}}

cm = CompMedia.new(input_hash)

object = cm.deep_open_struct(cm.merged_h)

p object.marshal_dump    # {:a=>#<OpenStruct dc=2, b=1>, :dd=>#<OpenStruct de=4>}
p object.a               # <OpenStruct dc=2, b=1>
p object.a.marshal_dump  # {:dc=>2, :b=>1}
p object.a.b             # 1
p object.a.dc            # 2
p object.dd              # <OpenStruct de=4>

显然,我还没有找到一种方法来以简单的方式从 openstruct 对象中检索嵌套的哈希元素。 我的目标是创建一个类,该类将使用类中包含的默认(嵌套)哈希和(嵌套)输入哈希进行初始化。此外,我希望能够在类中添加处理散列的方法。我还没到呢。

另一方面,我可以只使用合并的哈希值,尽管符号稍显繁琐:

class CompMedia
    attr_accessor :merged_h

    def initialize(hash)
        defaults = {a: {dc: 2}, dd: {de: 4}}
      @merged_h = {}
      deep_update(merged_h, defaults)
      deep_update(merged_h, hash)
      @merged_h
    end

    def deep_update(dest, src)
      src.each do |key, value|
        if value.is_a?(Hash)
          dest[key] = {} if !dest[key].is_a?(Hash)
          deep_update(dest[key], value)
        else
          dest[key] = value
        end
      end
    end

    def multiply_by(k)
        merged_h[:a][:dc] * k
    end

end

input_hash = {a: {b: 1}}

cm = CompMedia.new(input_hash)
p cm.merged_h           # {:a=>{:dc=>2, :b=>1}, :dd=>{:de=>4}}
p cm.merged_h[:a]       # {:dc=>2, :b=>1}
p cm.merged_h[:a][:dc]  # 2
p cm.merged_h[:dd]      # {:de=>4}
p cm.multiply_by(10)    # 20

我会考虑最后一个版本作为我的解决方案,除非有人可以使用 OpenStruct 使代码正常工作,我更愿意这样做。

【问题讨论】:

    标签: ruby hash


    【解决方案1】:

    这里有一些代码可以满足您的需求,但我放弃了子类化 OpenStruct 的想法,因为我不确定这是否是个好主意。另外,我自己实现了 deep_merge,因为它很容易实现,但如果您愿意,可以尝试使用 ActiveSupport 提供的版本。

    require 'ostruct'
    
    # Merges two hashes that could have hashes inside them.  Default
    # values/procs of the input hashes are ignored.  The output hash will
    # not contain any references to any of the input hashes, so you don't
    # have to worry that mutating the output will affect the inputs.
    def deep_merge(h1, h2)
      result = {}
      deep_update(result, h1)
      deep_update(result, h2)
      result
    end
    
    def deep_update(dest, src)
      src.each do |key, value|
        if value.is_a?(Hash)
          dest[key] = {} if !dest[key].is_a?(Hash)
          deep_update(dest[key], value)
        else
          dest[key] = value
        end
      end
    end
    
    def deep_open_struct(hash)
      result = OpenStruct.new
      hash.each do |key, value|
        if value.is_a?(Hash)
          result[key] = deep_open_struct(value)
        else
          result[key] = value
        end
      end
      result
    end
    
    input_hash = {a: {b: 1}}
    defaults = {a: {dc: 2}, dd: {de: 4}}
    object = deep_open_struct(deep_merge(defaults, input_hash))
    p object.a.b
    p object.a.dc
    p object.a.to_h
    

    【讨论】:

    • 谢谢,这行得通,但我相信我需要一个包含默认哈希的类,并使用合并的哈希(默认 + 输入)进行初始化。原因是我想包含处理哈希内容的其他方法。我正在使用您的代码处理它,如果我发现了什么,我会发布它。为什么子类化 OpenStruct 是个坏主意?
    • 拥有一个包含默认哈希的类应该不会太难。您可以将本文中的代码保持原样,并创建一个在内部使用它的类。
    • 我做到了。但我正在努力使用该类的方法来访问合并的哈希
    • 将哈希值存储在实例变量中,添加类似def hash; @hash; end的方法。
    • 我还有一些问题。我今天会发布它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-16
    相关资源
    最近更新 更多