【发布时间】:2010-09-08 16:19:53
【问题描述】:
我正在寻找一种优雅的方法来将存储在 Hash 中的值分配给预先存在的对象。 为了清楚起见,如果我有一个对象,比如 obj 有两个属性,比如名字和年龄,我想从哈希中分配这个值而不做类似的事情:
obj.name = hash[:name]
obj.age = hash[:age]
感谢您的关注。 西蒙娜
【问题讨论】:
我正在寻找一种优雅的方法来将存储在 Hash 中的值分配给预先存在的对象。 为了清楚起见,如果我有一个对象,比如 obj 有两个属性,比如名字和年龄,我想从哈希中分配这个值而不做类似的事情:
obj.name = hash[:name]
obj.age = hash[:age]
感谢您的关注。 西蒙娜
【问题讨论】:
最好的办法可能是简单地定义一个像 update_attributes 这样的方法,它接受一个哈希并在类的实例方法中执行它。
扩展其他人所写的内容以及您似乎需要的内容,我认为您最好的选择是:
hash.keys.each do |key|
m = "#{key}="
obj.send( m, hash[key] ) if obj.respond_to?( m )
end
这将说明:
【讨论】:
key 是字符串x 而hash[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]。
不知道优雅,但你可以:
[:name, :age].each do |att|
obj.send("#{att}=", hash[att])
end
【讨论】:
这是我多年来一直使用的一个非常易于输入的模式:
{
first_name: "John",
last_name: "Smith",
...
}.each {|k, v| send("#{k}=", v)}
【讨论】:
public_send
attr= 是私有的情况。
使用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 无效。
【讨论】:
obj.methods.grep(/[^!=]=$/).each {|attr| obj.send(attr, hash[attr]) }
【讨论】:
if hash.has_key?(attr),以防您在哈希中没有对象的所有属性。
=$ 部分),但还有一些其他方法也以等号(即==、=== 和!=),所以我只匹配那些没有! 或= 作为倒数第二个字符的字符(即[^!=] 部分) .
您可以直接在对象上调用assign_attributes方法。
obj.assign_attributes(hash)
【讨论】: