2

I have the following code and it works fine.

months = {"Feb"=>["day1", "day2"]}

i = 0
while i < 5 do
   months["Feb"][i] = "day#{i}"
   i += 1
end
puts months

$> {"Feb"=>["day0", "day1", "day2", "day3", "day4"]}

But if I remove the first line where I intialize the hash or try to add values to a different hash key on the fly I get an error that 'months' in undefined.

So I am confused. Will Ruby not allow you to arbitrarily add keys to a hash? I am used to Perl where you can just start making hashes and arrays as you please. But I know that Perl treats hashes & arrays as seperate objects where as Ruby everything is considered the same, so I didn't know if it had something to do with that (although the Perl way is probably "sloppy" ^_^ )

1
  • you can add keys and values on the fly, but you have to initialize the first empty array Commented Oct 13, 2012 at 11:29

4 Answers 4

1

In Ruby you must initialize a variable before using it (looks like a sound policy...). Note also that what you wrote is not idiomatic Ruby, an alternative:

months = {"Feb" => 0.upto(4).map { |i| "day#{i}" }}

To update:

months["Feb"] = 0.upto(4).map { |i| "day#{i}" }
Sign up to request clarification or add additional context in comments.

Comments

1

You always need to initialize variables in Ruby. But you can initialize hashes as following:

# Using {} to creates an empty hash
months = {}

# or create a new empty hash object from Hash class
months = Hash.new

# instead of
months = {"Feb"=>["day1", "day2"]}

To initialize a Array within a Hash:

# Array.new creates a new Array with size 5
# And values within Hash.new block are the default values for the hash
# i.e. When you call the Hash#[], it creates a new array of size 5
months = Hash.new { |hash, key| hash[key] = Array.new(5) }
puts months        #=> {}
puts months["Feb"] # Here the Hash creates a new Array inside "Feb" key
puts months        #=> {"Feb" => [nil, nil, nil, nil, nil]}
puts months["Feb"][3] = "Day3"
puts months        #=> {"Feb" => [nil, nil, nil, "Day3", nil]}

To do the same using an undefined array size:

months = Hash.new { |hash, key| hash[key] = [] }
puts months        #=> {}
months["Feb"].push "Day0"
puts months        #=> {"Feb" => ["Day0"]}
months["Feb"].push "Day1"
puts months        #=> {"Feb" => ["Day0", "Day1"]}

I think a more elegant approach is to use the map method to build your array of days before bind it to "Feb" key:

months = {"Feb" => (0..4).map{ |i| "day#{i}" }}
# => {"Feb"=>["day0", "day1", "day2", "day3", "day4"]}

If you don't want to type the month name you can require the Date class and get the month name passing an Fixnum:

require 'date'
months = {Date::ABBR_MONTHNAMES[2] => (0..4).map{ |i| "day#{i}"}}
# => {"Feb"=>["day0", "day1", "day2", "day3", "day4"]}

To generate the same structure for all months you can do:

days   = (0..4).map{ |d| "day#{d}"}
months = (1..12).map{ |m| {Date::ABBR_MONTHNAMES[m] => days }}
# => {"Jan"=>["day0", "day1", "day2", "day3", "day4"],
#     "Feb"=>["day0", "day1", "day2", "day3", "day4"],
#     ...
#     "Dec"=>["day0", "day1", "day2", "day3", "day4"]}

Some useful documentation:

2 Comments

I only used calender words as an example. The .map method does work, but what if I just wanted to insert one element into the hash of array? I don't want to do a count I just want to insert a value to hashname[key][2]. Its seems like Ruby's complaint is that it wants the key to exist. It doesn't seem to have any problem with adding array elements, but if the key doesn't exist then it complains. Is there an easy way to create a key within the hash and then start populating from there?
I improved my answer, I hope it can help you.
0
# the ||= set this to an empty hash({}) if it hasn't been initialized yet
months ||= {}

#updated as you like, use symbols :feb over string "feb" as the key (read this in other questions in this website)
months[:feb] = (0..4).map {|n| "day#{n}"}

p months  #==> {:feb=>["day0", "day1", "day2", "day3", "day4"]}

Comments

0

Based on what you guys said I messed around and it looks like Ruby wants the key to exist before you can start assigning values to that key.

hashname = Hash.new
hashname[:foo] = {}
hashname[:foo][3] = "test"
hashname[:foo][1] = "test1"
hashname[:bar] = {}
hashname[:bar][3] = "test3"
puts hashname
puts hashname[:foo][1]

C:\Ruby>scratch.rb
{:foo=>{3=>"test", 1=>"test1"}, :bar=>{3=>"test3"}}
test1

Once the key exists, you can start assigning values as you please. Although in this case you can see that I am creating a "hash of hashes" instead of a "hash of arrays" as the .map method does which still eludes me as to why.

Thanks for the help!

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.