2010-09-08 3 views
10

Я ищу элегантный способ присвоить значение, хранящееся внутри хэша, в ранее существовавшем объекте. Просто чтобы быть ясно, если у меня есть объект, скажем, OBJ с двумя атрибутами, скажем, имя и возраст, я хочу, чтобы назначить эти значения, поступающие из хэша, не сделать что-то вроде:DRY способ присвоить значения хэша объекту

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

Спасибо за ваше внимание. Simone

ответ

9

Лучше всего, вероятно, чтобы просто определить метод как update_attributes который принимает хэш и делает это внутри метода экземпляра класса ,

Расширение на то, что другие написали и то, что вы, кажется, нужно, я думаю, вам лучше всего будет:

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

Это составит:

  • не имеющий все атрибуты класса в хэш во все времена, и
  • любое количество ключей в хэш (а не просто: имя и т.д.)
+0

Не могли бы вы объяснить мне, почему у вас есть wirte m = "# {key} =" insted of m = "# {key}"? –

+0

Ну, это сложно объяснить, но на самом деле очень просто. Сначала давайте скажем для объяснения того, что 'key' является строкой' x' и 'hash [key]' is '1' ... Теперь в Ruby код' obj.x = 1' фактически эквивалентен ' obj.x = (1) '. Поэтому в следующей строке, когда я делаю 'obj.send (m, ...)', мне нужно это _assign_ для этого свойства. Другими словами, если бы я не поместил знак равенства в имя метода, он бы назвал 'obj.x (1)' и 'x' просто не принимает параметр (потому что это свойство). То, что я хочу, это 'obj.x = (1)', и поэтому мне нужно '# {key} =' – rfunduk

+2

Вы также можете сделать: 'hash.each do | key, value |' then use 'value' вместо' хэш [ключ] '. – PhilT

2

Не знаю, элегантный, но вы можете сделать:

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

Ваш метод выглядит хорошо, но что, если у меня есть больше, чем имя и возраст? –

1
obj.methods.grep(/[^!=]=$/).each {|attr| obj.send(attr, hash[attr]) } 
+0

Возможно, хотите хотя бы добавить 'if hash.has_key? (Attr)' там, если у вас нет каждого атрибута объекта в хэше. – rfunduk

+0

@thenduks: Насколько я могу декодировать спецификацию, у объекта есть два атрибута, которые всегда находятся в хеше. Но, конечно, вопрос не совсем точно сформулирован. Вероятно, по крайней мере, у нее должна быть спецификация и набор тестов для правильного ответа. –

+0

Ваш метод кажется очень аккуратным, но не могли бы вы объяснить мне, что означает регулярное выражение? –

2

Использование 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 нет.

3

Вот довольно легко набираемые шаблон я использую в течение многих лет:

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

Я бы использовал 'public_send' – kode

+0

@kode, звучит разумно, но на протяжении многих лет я ** никогда не сталкивался с ситуацией, когда' attr = 'были частными. –

0

Вы можете непосредственно вызвать метод assign_attributes на объекте.

obj.assign_attributes(hash) 
Смежные вопросы