-1

I am trying to create a hash that has a key and an array of values associated with the key. So I was doing something like this

 hash = Hash.new

and then later when I have a key I would do this

 hash[key] << new_thing

but this fails if hash[key] is nil , so I added this statement before trying to append the new_thing to the array.

 hash[key] = Array.new unless hash[key] 
 hash[key] << new_thing

I thought I could eliminate the added statement by changing the definition of hash to

hash = Hash.new Array.new 

but this does not work because each entry shares the same array, what I really need is this

hash = Hash.new { |h,k| h[k] = Array.new }

I know I am probably splitting hairs but, I am just curious, what would be the "ruby" way to do this?

EDIT: Ultimately what I ended up doing is using group_by. I realize it was not obvious from my question that I was trying to use this structure to group objects with the same property together in a hash. I am including this to encourage others that may find themselves using a similar structure for a similar purpose to check out group_by

["A", 1, "b", 3].group_by { |i| i.class }
=> {String=>["A", "b"], Fixnum=>[1, 3]}
nPn
  • 16,254
  • 9
  • 35
  • 58
  • 3
    What's wrong with your last `default_proc` version? – mu is too short Dec 30 '13 at 04:13
  • It works, but I guess I was thinking it might be more confusing than the added statement. I figured this must come up quite a bit and there were some preferred way to do this – nPn Dec 30 '13 at 04:18
  • What is `hash{key]`? It is not even valid Ruby code. – sawa Dec 30 '13 at 04:22
  • @sawa: A simple typo? – mu is too short Dec 30 '13 at 04:24
  • @sawa: Given the usual location of `{` and `[` on a keyboard it seems pretty obvious that it was a typo. Perhaps if you asked if `hash{key]` was a typo you'd get friendlier responses. – mu is too short Dec 30 '13 at 04:26
  • @muistooshort You may or may not be right about it, but I ask neutrally without making any guess to make it easy for me. Whether or not it is a typo, it is the OP's responsibility to straighten it. Readers should not have to think extra. – sawa Dec 30 '13 at 04:28
  • yes it was a simple typo, thanks for the edit mu is too short – nPn Dec 30 '13 at 04:31

3 Answers3

4

The Ruby way to set an empty array as the default value for a Hash would be to use the default_proc version that is in your question:

hash = Hash.new { |h,k| h[k] = [ ] }

For example:

> hash = Hash.new { |h,k| h[k] = [ ] }
> hash[:pancakes] << 6
> hash
 => {:pancakes=>[6]} 

Using standard Ruby features when writing Ruby seems pretty Rubyish to me.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
0

I've seen people doing this more often:

(h[key] ||= []) << value

As that's the explicit way, and rubists like explicitness.

Your version is also common though.

By the way, you can set the defaults on existing hash:

h.default_proc = ->(h, k){ h[k] = [] }

or

h.default = 0

And I find that being a bit cleaner, but not as widely used.

Bart
  • 2,606
  • 21
  • 32
  • Huh, thats misleading. I'll update the answer than. – Bart Dec 30 '13 at 04:29
  • nPn, one consequence of using Bartosz' first approach is that `puts "hello" if h[7]` gives the intended result (so long as `h` does not contain `nil` values), whereas `h[7]` will always be true if the hash is created with a default value, and `h => [7]` (after `h => {}`) if created with `Hash.new {|h,k| h[k] = []}`. Perhaps the lesson is to always use [`Hash#key?`](http://www.ruby-doc.org/core-2.1.0/Hash.html#method-i-key-3F) (aka `has_key?`) to check for the presence of a key. – Cary Swoveland Dec 30 '13 at 04:55
-3

you can set default value

numbers = Hash.new(0)
brianadams
  • 233
  • 1
  • 5