9

Can I create a Ruby Hash from a block?

Something like this (although this specifically isn't working):

foo = Hash.new do |f|
  f[:apple] = "red"
  f[:orange] = "orange"
  f[:grape] = "purple"
end
user94154
  • 16,176
  • 20
  • 77
  • 116

5 Answers5

18

In Ruby 1.9 (or with ActiveSupport loaded, e.g. in Rails), you can use Object#tap, e.g.:

foo = Hash.new.tap do |bar|
  bar[:baz] = 'qux'
end

You can pass a block to Hash.new, but that serves to define default values:

foo = Hash.new { |hsh, key| hsh[key] = 'baz qux' }
foo[:bar]   #=> 'baz qux'

For what it's worth, I am assuming that you have a larger purpose in mind with this block stuff. The syntax { :foo => 'bar', :baz => 'qux' } may be all you really need.

wuputah
  • 11,285
  • 1
  • 43
  • 60
  • I have a fixture file that is more or less a bunch of ActiveRecord create method calls. Wanted to pretty it up for readability. – user94154 Sep 08 '10 at 17:30
13

I cannot understand why

foo = {
  :apple => "red",
  :orange => "orange",
  :grape => "purple"
}

is not working for you?

I wanted to post this as comment but i couldn't find the button, sorry

mikej
  • 65,295
  • 17
  • 152
  • 131
gkaykck
  • 2,347
  • 10
  • 35
  • 52
  • 1
    you need 50 reputation before you can post comments. You should be able to leave comments now your answer has been upvoted. – mikej Sep 08 '10 at 17:08
5

Passing a block to Hash.new specifies what happens when you ask for a non-existent key.

foo = Hash.new do |f|
  f[:apple] = "red"
  f[:orange] = "orange"
  f[:grape] = "purple"
end
foo.inspect # => {}
foo[:nosuchvalue] # => "purple"
foo # => {:apple=>"red", :orange=>"orange", :grape=>"purple"}

As looking up a non-existent key will over-write any existing data for :apple, :orange and :grape, you don't want this to happen.

Here's the link to the Hash.new specification.

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
3

What's wrong with

foo = {
  apple:  'red',
  orange: 'orange',
  grape:  'purple'
}
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
1

As others have mentioned, simple hash syntax may get you what you want.

# Standard hash
foo = {
  :apple => "red",
  :orange => "orange",
  :grape => "purple"
}

But if you use the "tap" or Hash with a block method, you gain some extra flexibility if you need. What if we don't want to add an item to the apple location due to some condition? We can now do something like the following:

# Tap or Block way...
foo = {}.tap do |hsh|
  hsh[:apple] = "red" if have_a_red_apple?
  hsh[:orange] = "orange" if have_an_orange?
  hsh[:grape] = "purple" if we_want_to_make_wine?
}
Ondecko
  • 11
  • 1