【问题标题】:how to map Class to a Hash and vice versa如何将类映射到哈希,反之亦然
【发布时间】:2011-11-10 20:33:28
【问题描述】:

我有一门课,像这样的:

class A
  attr_accessor(:field2)
  attr_accessor(:field1)
end

使用从类实例中获取的键和值生成哈希的最佳方法是什么?

用该哈希值填充类 A 的实例的最佳方法是什么?

=====

我可能正在寻找类似于 JavaBeans 内省的东西,它会给我数据对象字段的名称,然后根据此信息执行逻辑。 Ruby 是一种非常现代的灵活和动态语言,我拒绝承认它不会让我做我可以用 Java 轻松完成的事情 ;-)

=====

最后我发现 Struct 是最好的选择:

a = {:a => 'qwe', :b => 'asd'}

s = Struct.new(*a.keys).new(*a.values) # Struct from Hash

h = Hash[*s.members.zip(s.values).flatten] # Hash from Struct

【问题讨论】:

  • 你必须在某个地方定义field1和field2是你感兴趣的字段,一个类有很多方法...
  • 是的,很多方法,但是我对字段感兴趣,真的没有办法判断一个类是否只有两个字段吗?类似于 JavaBeans 自省的东西?
  • AFAIK attr_accessor 透明地添加了 getter/setter,因此您无法从“普通”方法/实例变量中区分它们。
  • @Oleg 它可以,但是如果你对字段感兴趣,并且想要一个通用的解决方案,你不能依赖方法.在 Java 中也是如此:getter 可以在没有相应属性的情况下存在(并且经常这样做,例如,通过 JSP EL 向视图层公开数据)。
  • 我不确定你的最终目标是什么:如果是序列化/反序列化,为什么不使用 JSON 或 YAML 并使用已经存在的东西?

标签: java ruby


【解决方案1】:

开始玩的东西:

a = A.new
a.field1 = 1
a.field2 = 2
methods = a.public_methods(false).select { |s| s.end_with?("=") }
attributes = Hash[methods.map { |m| [m, a.send(m)] }]
=> {"field1"=>1, "field2"=>2}

如果您想要对 getter/setter 对进行更细粒度的检测:

methods = a.public_methods(false).group_by { |s| s.split("=")[0] }.
  map { |k, vs| k if vs.size == 2 }.compact

关于第二个问题:

attributes = {"field1"=>1, "field2"=>2}
a = A.new
a.each { |k, v| a.send(k+"=", v) }
=> #<A:0x7f9d7cad7bd8 @field1=1, @field2=2>

但是,您似乎想使用 StructOpenStruct 之类的东西。

【讨论】:

【解决方案2】:

要散列的类。如果需要,当然可以将其写为 A 中的方法。

foo = A.new
foo.field1 = "foo"
foo.field2 = "bar"
hash = {}
foo.instance_variables.each {|var| hash[var.to_s.delete("@")] = foo.instance_variable_get(var) }
p hash
 => {"field1"=>"foo", "field2"=>"bar"} 

哈希到类:扩展 A 的 initialize。借自http://pullmonkey.com/2008/01/06/convert-a-ruby-hash-into-a-class-object/

class A
  def initialize(hash)
    hash.each do |k,v|
      self.instance_variable_set("@#{k}", v)
    end
  end
end

那么你可以:

hash = { :field1 => "hi" }
foo = A.new(hash)
 => #<A:0x00000002188c40 @field1="hi"> 

【讨论】:

    【解决方案3】:
     f.instance_variables.inject({}) { |m, v| m[v] = f.instance_variable_get v; m }
    

    虽然这会在属性符号中为您提供@;如果它很重要,你可以在作业中去掉它。反之亦然;遍历键并使用instance_variable_set

    您还可以查询以 = 结尾的方法,如果您向其中任何一个添加逻辑而不是依赖于 attr_accessor 创建的那些,这将更加健壮。

    【讨论】:

    • 这是一个合理的解决方案,尽管最终 OP 将拥有比 attr_accessor 创建的实例变量更多的实例变量。
    • @tokland 我不知道你是怎么知道的。
    • 对不起,我的意思是“可能有”,而不是“将有”,当然我不知道他的完整课是什么样子的。
    • 一个问题,戴夫,我在你的回答中看到你似乎更喜欢注入来构建哈希而不是 Hash::[]... 为什么?效率?
    • @tokland 使用[],你要么需要一个映射、一个元组数组,要么需要一个扁平的有序数组——对于上述内容,我发现injectHash[f.instance_variables.collect { |v| [v, f.instance_variable_get(v)] }] 更容易阅读,即使注入的时间长了几个字符 - 关闭 )]}] 让我的眼睛流泪。请注意,可能有更清洁的方法;没想到。
    猜你喜欢
    • 2011-06-24
    • 2018-02-05
    • 2017-11-17
    • 1970-01-01
    • 1970-01-01
    • 2011-06-15
    • 1970-01-01
    • 2022-06-11
    相关资源
    最近更新 更多