I have an array of hashes
Eg:
cars = [{:company => "Ford", :type => "SUV"},
{:company => "Honda", :type => "Sedan"},
{:company => "Toyota", :type => "Sedan"}]
# i want to fetch all the companies of the cars
cars.collect{|c| c[:company]}
# => ["Ford", "Honda", "Toyota"]
# i'm lazy and i want to do something like this
cars.collect(&:company)
# => undefined method `company'
I was wondering if there is a similar shortcut to perform the above.
I believe your current code cars.collect{|c| c[:company]}
is the best way if you're enumerating over an arbitrary array. The method you would pass in via the & shortcut would have to be a method defined on Hash since each object in the array is of type Hash
. Since there is no company
method defined for Hash
you get the "undefined method 'company'" error.
You could use cars.collect(&:company)
if you were operating on an Array of Cars though, because each object passed into the collect
block would be of type Car
(which has the company method available). So maybe you could modify your code so that you use an array of Cars instead.
You could convert the hashes to OpenStructs.
require 'ostruct'
cars = [{:company => "Ford", :type => "SUV"},
{:company => "Honda", :type => "Sedan"},
{:company => "Toyota", :type => "Sedan"}]
cars = cars.map{|car| OpenStruct.new(car)}
p cars.map( &:company )
#=> ["Ford", "Honda", "Toyota"]
It's impossible to use in your case, because in collect you use method []
and argument :company. The construction &:company takes labels :company
and converts to Proc
, so it's only one argument - the name of method.
Unfortunately Ruby hashes can't do that. Clojure maps on the other hand have functions for each key which return the corresponding value, which would be easy enough to do if you are so inclined (you should also add the corresponding respond_to?
method):
>> class Hash
.. def method_missing(m)
.. self.has_key?(m) ? self[m] : super
.. end
.. end #=> nil
>> cars.collect(&:company) #=> ["Ford", "Honda", "Toyota"]
>> cars.collect(&:compay)
NoMethodError: undefined method `compay' for {:type=>"SUV", :company=>"Ford"}:Hash
Note: I'm not advising this, I'm just saying it's possible.
Another horrible monkeypatch you shouldn't really use:
class Symbol
def to_proc
if self.to_s =~ /bracket_(.*)/
Proc.new {|x| x[$1.to_sym]}
else
Proc.new {|x| x.send(self)}
end
end
end
cars = [{:company => "Ford", :type => "SUV"},
{:company => "Honda", :type => "Sedan"},
{:company => "Toyota", :type => "Sedan"}]
cars.collect(&:bracket_company)
来源:https://stackoverflow.com/questions/5473325/question-on-ruby-collect-method