20

I have an object that has attributes name and data, among others. I want to create a hash that uses the name as the key and the data (which is an array) as the value. I can't figure out how to reduce the code below using map. Is it possible?

def fc_hash
  fcs = Hash.new
  self.forecasts.each do |fc|
    fcs[fc.name] = fc.data
  end
  fcs
end
6
  • 1
    map returns an array, and will not return a hash. But that said, there isn't really anything wrong with this code. Why not use it just like you have it? Commented Apr 2, 2013 at 18:54
  • Hash[*self.forecasts.map{ [fc.name, fc.data]}.flatten] Commented Apr 2, 2013 at 18:55
  • What is forecasts? Particularly, what is its class? Commented Apr 2, 2013 at 19:00
  • 1
    @sawa It seems reasonable to assume it's an array of Forecast instances, doesn't it? Commented Apr 2, 2013 at 19:02
  • In Ruby, { } is almost always preferable to Hash.new. The only reason to call the formal constructor is for passing in defaults, like Hash.new(0) or Hash.new { |h,k| ... }. Commented Apr 2, 2013 at 19:08

5 Answers 5

24

Use Hash[]:

Forecast = Struct.new(:name, :data)
forecasts = [Forecast.new('bob', 1), Forecast.new('mary', 2)]
Hash[forecasts.map{|forecast| [forecast.name, forecast.data]}]
# => {"mary"=>2, "bob"=>1}
Sign up to request clarification or add additional context in comments.

Comments

14
def fc_hash
 forecasts.each_with_object({}) do |forecast, hash|
    hash[forecast.name] = forecast.data
  end
end

1 Comment

Yes, you both do seem very enamored of it :p
3

I always use inject or reduce for this:

self.forecasts.reduce({}) do |h,e|
  h.merge(e.name => e.data)
end

Comments

1
Hash[*self.forecases.map{ [fc.name, fc.data]}.flatten]

4 Comments

Job security: 1, Readability: 0.
if the reader understands what map and flatten is this is pretty easy to read
You need to understand not only what Hash[] is but also what * does in that context. It's clever and concise, but a bit too intense for one line.
But you don't need the splat or the flatten, they're just noise as Hash[] is quite happy with an array-of-arrays. You're also missing something in the map block.
0

With Ruby 2.1 and onward, you could also use Array#to_h

self.forecasts.to_h { |forecast| [forecast.name, forecast.data] }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.