1

This is my code:

def mainFunction    
    @notes=Hash.new(Array.new)
    @notes["First"].push(true)
    @notes["First"].push(false)
    @notes["First"].push(true)
    @notes["Second"].push(true)
    output @notes.size.to_s
end

Why is the output 0? It should be 2 since notes has two keys, "First" and "Second".

sawa
  • 165,429
  • 45
  • 277
  • 381
markzzz
  • 47,390
  • 120
  • 299
  • 507
  • In your example there are no keys to push to. You have to first of all initialize the key which points to an array like this `@notes = {"First" => []}` and then when you do `@notes["First"].push(true)` it would work. – Pramod Solanky Oct 31 '14 at 12:44
  • Very similar to [Modifying the default hash value](http://stackoverflow.com/q/9492889/479863). – mu is too short Oct 31 '14 at 17:48
  • [How to assign hash\[“a”\]\[“b”\]= “c” if hash\[“a”\] doesn't exist?](http://stackoverflow.com/q/5878529/479863) is also similar or at least the answer should help clear things up. – mu is too short Oct 31 '14 at 17:51
  • Your question is similar to [this one](http://stackoverflow.com/questions/26857356/why-the-array-is-not-getting-modified-in-ruby). You may wish to review the answers there for additional insights. If you choose to not use `Hash.new {|h, k| h[k] = []}`, you might consider whether you want `@notes=Hash.new(Array.new)` rather than `@notes=Hash.new(Array.new)`. With the latter, all keys share the same array; with the latter, each has its own array. Don't forget to select an answer (if you found any helpful). – Cary Swoveland Nov 14 '14 at 19:51

5 Answers5

3

When initializing Hash values when accessed for the first time (ie. pushing onto key'd values that don't yet exist), you need to set the key on the Hash when it is requested.

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

For reference, see the following result in the ruby repl initializing the Hash as you have

irb(main):063:0> @notes = Hash.new(Array.new)
=> {}
irb(main):064:0> @notes[:foo].push(true)
=> [true]
irb(main):065:0> @notes.has_key?(:foo)
=> false
irb(main):066:0> puts @notes.size
=> 0

and now the proper way.

irb(main):067:0> @notes = Hash.new {|h, k| h[k] = []}
=> {}
irb(main):068:0> @notes[:foo].push(true)
=> [true]
irb(main):069:0> @notes.has_key?(:foo)
=> true
irb(main):070:0> @notes.size
=> 1
deefour
  • 34,974
  • 7
  • 97
  • 90
  • Deefour, Sorry for contacting you like this, but I didn't know how else to get in touch with you. I noticed you voted to put my question http://stackoverflow.com/questions/26743387/how-to-increment-upvote-total-in-my-view-after-successful-ajax-request-to-upvote on hold. It's been edited to be more clear now, and I badly need an answer soon. Please consider withdrawing your vote, so I can place a bounty on it as soon as possible. –  Nov 06 '14 at 17:39
1

The following statement:

@notes=Hash.new(Array.new)

Creates a hash with a default value: Array.new. When you access the hash with an unknown key, you will receive this default value.

Your other statements therefor change that default array:

@notes["First"].push(true)

This adds true to the default (empty) array.

You need to initialize the "First" array first:

@notes["First"] = []

And then you can add values:

@notes["First"].push(true)

Or more "rubyish":

@notes["First"] << true

The size now is:

@notes.size # => 1
rdvdijk
  • 4,400
  • 26
  • 30
0

Hash.new(object) method call form just return object as a default value but does not update the hash.

You are looking for a block form where you can do the update:

@notes=Hash.new {|h, k| h[k] = []}
David Unric
  • 7,421
  • 1
  • 37
  • 65
0

@notes = Hash.new(Array.new) sets the default value for all elements to the same array. Since @notes contains no key "First", it returns that default value, and the .push adds to the end of it. But the key has not been added to the hash. The other pushes add to the end of the same default array.

Rob
  • 26
  • 4
0

Read this Hash.new(obj). It states:

If obj is specified, this single object will be used for all default values.

Hence, when you do: @notes=Hash.new(Array.new). The default object will be an array.

@notes.default # => []
@notes['First'].push(true)
@notes.default # => [true]
@notes['First'].push(false)
@notes.default # => [true, false]

But, there won't be any entry as a key in @notes, of course you use access those values by giving any key(which may or may not exist), like this:

@notes['unknown_key'] #=> [true, false]
Surya
  • 15,703
  • 3
  • 51
  • 74