Given I have an object which responds so some methods. I want to collect some of them in a Hash, I am always writing a snipped like this.
class Person
# ...
def some_selected_attributes
attrs = {}
[:first_name, :last_name, :zip].each do |attr|
attrs[attr] = self.__send__(attr)
end
attrs
end
end
I belive there is a more elegant way to do this.
:wq!
If you really want a hash, LBg has given you some fine patterns. But you may want to consider making small struct objects instead of primitive hashes. Structs behave like hashes for indexing and enumeration but also have accessor methods so they can be used with send and dot notation.
class Person
# ...
SomeSelectedPersonAttrs = Struct.new :first_name, :last_name, :zip
def some_selected_attributes
SomeSelectedPersonAttrs[ * SomeSelectedPersonAttrs.members.map{|a| send a } ]
end
end
pa = person.some_selected_attributes
pa.first_name # => "Joe"
pa[:first_name] # => "Joe"
pa['first_name'] # => "Joe"
p.zip = 12345 # sets zip to 12345
p[:zip] = 12345
p['zip'] = 12345
pa.values # => ["Joe","Blow",12345]
pa.each_pair {|k,v| ... }
As there's only three attributes, I see there's no reason to don't write it directly:
def some_selected_attributes
{
first_name: first_name,
last_name: last_name,
zip: zip
}
end
You can also do it with inject
:
def some_selected_attributes
[:first_name, :last_name, :zip].inject({}) do |hash, attr|
hash[attr] = __send__ attr
hash
end
end
Another option, using Hash[]
:
def some_selected_attributes
Hash[[:first_name, :last_name, :zip].map {|attr| [attr, __send__ attr] }]
end
inject
goes in the right direction, but is hard to read. Any other ideas - iblue 2012-04-05 23:51
each_with_object
instead of inject
to get rid of the line with only hash
on it: [:first_name, :last_name, :zip].each_with_object({}){|attr,hash| hash[attr] = __send__ attr}
Abe Voelker 2012-04-06 01:12
I think that there is a better way to accomplish what you're trying to do. It doesn't make much sense to write a method to return a hash of arguments that are already in the class. You could just make the instance variables or methods accessible using attr_accessor:
class Person
attr_accessor :first_name, :last_name, :zip
end
Then you don't need the some_selected_attributes() method. You could just access the variables or methods directly:
p = Person.new
p.first_name # => value of first_name
If you wanted a hash, you could just create it:
p = Person.new
hash = {:first_name => p.first_name, :last_name => p.last_name, :zip => p.zip }
I'm not sure why you'd want to build this hash either. It seems like the're a more "object oriented" way to accomplish whatever you're trying to do (by passing the whole object as an argument).
Instead of working with a hash of selected fields, I'd suggest working with whole objects.
h = {}; ...;return h;
just for readabilit - Deradon 2012-04-05 23:43