14

I've been reading the Ruby docs, and looking at some other posts on the issue, but I am still wondering about this:

#counts each number in an array once
array = [1,1,2,5,3,2,5,3,3,3]
numbers = {}
array.each { |num| numbers[num] += 1 }

=> in `block in mode': undefined method `+' for nil:NilClass (NoMethodError)

In the Hash documentation the default value for a Hash is nil, which is why I am getting this error I assume. Is there a better way to insert each key/(value += 1) into the numbers array?

3
  • Suppose you execute numbers = Hash.new(0) and then numbers[num] += 1 when num is not a hash key. Since you gave a default value of 0 when creating the hash, when Ruby finds that num is not a hash key, it creates a new hash element h[num] = 0. It then executes h[num] += 1 # => 1 and Bob's your uncle. Commented Nov 6, 2013 at 4:14
  • Thanks, that makes a lot more sense. Commented Nov 6, 2013 at 4:35
  • In case you are wondering, when new is given a default argument, Ruby won't create a hash element unless an assignment is performed. If h = Hash.new(0), for example, h[6] will equal nil in the if statement if h[6] == 0, resulting in the condition being false, and no hash element with key = 6 will be created (which otherwise would cause the if condition to be evaluted as true). Commented Nov 6, 2013 at 5:13

4 Answers 4

30

Try passing a default value to your new hash as such

numbers = Hash.new(0)
Sign up to request clarification or add additional context in comments.

Comments

2

You can explicitly do it this way as well:

array.each { |num| numbers[num] = (numbers[num] || 0) + 1 }

Comments

2

Variant with inject and Hash.new(0)

  numbers = [1,1,2,5,3,2,5,3,3,3].inject(Hash.new(0)){|numbers, number| numbers[number] +=1; numbers}

Comments

1

Aside from using the Hash default, you could also try something with group_by:

array = [1,1,2,5,3,2,5,3,3,3]
numbers = Hash[*array.group_by { |i| i }.flat_map { |k, v| [k , v.size] }]

There's probably a better way if you play around with it some.

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.