【问题标题】:DRY way to assign hash values to an object将哈希值分配给对象的 DRY 方法
【发布时间】:2010-09-08 16:19:53
【问题描述】:

我正在寻找一种优雅的方法来将存储在 Hash 中的值分配给预先存在的对象。 为了清楚起见,如果我有一个对象,比如 obj 有两个属性,比如名字和年龄,我想从哈希中分配这个值而不做类似的事情:

obj.name = hash[:name]
obj.age = hash[:age] 

感谢您的关注。 西蒙娜

【问题讨论】:

    标签: ruby hash


    【解决方案1】:

    最好的办法可能是简单地定义一个像 update_attributes 这样的方法,它接受一个哈希并在类的实例方法中执行它。

    扩展其他人所写的内容以及您似乎需要的内容,我认为您最好的选择是:

    hash.keys.each do |key|
      m = "#{key}="
      obj.send( m, hash[key] ) if obj.respond_to?( m )
    end
    

    这将说明:

    • 没有始终在哈希中包含类的所有属性,并且
    • 散列中任意数量的键(不仅仅是:name等)

    【讨论】:

    • 你能解释一下为什么你必须写 m = "#{key}=" insted of m = "#{key}" 吗?
    • 嗯,解释起来很棘手,但实际上非常简单。首先,为了解释key 是字符串xhash[key]1...现在,在Ruby 中代码obj.x = 1 实际上等同于obj.x=( 1 )。因此,在我执行obj.send( m, ... ) 的下一行中,我需要将它assign 分配给该属性。换句话说,如果我没有将等号放在方法名称中,它会调用obj.x( 1 )x 根本不带参数(因为它是一个属性)。我想要的是obj.x=( 1 ),这就是为什么我需要#{key}=
    • 你也可以这样做:hash.each do |key, value| 然后使用value 而不是hash[key]
    【解决方案2】:

    不知道优雅,但你可以:

    [:name, :age].each do |att|
      obj.send("#{att}=", hash[att])
    end
    

    【讨论】:

    • 你的方法看起来不错,但是如果我有更多的名字和年龄呢?
    【解决方案3】:

    这是我多年来一直使用的一个非常易于输入的模式:

    {
      first_name: "John",
      last_name: "Smith",
      ...
    }.each {|k, v| send("#{k}=", v)}
    

    【讨论】:

    • 我会使用public_send
    • @kode,听起来很合理,但多年来我从未遇到过attr= 是私有的情况。
    【解决方案4】:

    使用instance_variable_set 是另一种选择。这是一个例子:

    class Test; attr_accessor :name, :age; end
    
    hash = {name: 'John', age: 52}
    
    obj = Test.new
    hash.each do |k,v|
      obj.instance_variable_set("@#{k}".to_sym, v) # :name is converted to :@name
    end
    
    p obj # => #<Test:0x007fd8cbb75b00 @name="John", @age=52> 
    

    唯一的技巧是 instance_variable_set 需要一个以 @ 开头的符号,因此 :@name 有效,但 :name 无效。

    【讨论】:

      【解决方案5】:
      obj.methods.grep(/[^!=]=$/).each {|attr| obj.send(attr, hash[attr]) }
      

      【讨论】:

      • 可能希望至少在其中添加一个if hash.has_key?(attr),以防您在哈希中没有对象的所有属性。
      • @thenduks:就我可以解码的规范而言,该对象有两个属性,它们总是在散列中。但是,当然,这个问题的措辞并不完全正确。它可能至少应该有一个规范和一个测试套件来正确回答。
      • 你的方法看起来很整洁,但是你能解释一下正则表达式代表什么吗?
      • @user442622:我想获取所有 setter 方法,即所有以等号结尾的方法(即 =$ 部分),但还有一些其他方法也以等号(即=====!=),所以我只匹配那些没有!= 作为倒数第二个字符的字符(即[^!=] 部分) .
      【解决方案6】:

      您可以直接在对象上调用assign_attributes方法。

      obj.assign_attributes(hash)
      

      【讨论】:

      • 这是一个特定于 Active Record 的 API,而不是一个 Ruby 标准库。
      猜你喜欢
      • 2012-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-13
      • 1970-01-01
      • 2021-07-28
      • 2015-07-05
      • 1970-01-01
      相关资源
      最近更新 更多